@skilder-ai/runtime 0.9.16 → 0.9.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/index.js +1318 -25
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -87716,10 +87716,14 @@ var OPTIMIZATION_INSIGHTS_TIMEFRAMES = [
|
|
|
87716
87716
|
];
|
|
87717
87717
|
var DEFAULT_OPTIMIZATION_INSIGHTS_LOOKBACK_SECONDS = 30 * 24 * 60 * 60;
|
|
87718
87718
|
|
|
87719
|
-
// ../common/src/
|
|
87720
|
-
|
|
87721
|
-
|
|
87722
|
-
|
|
87719
|
+
// ../common/src/node-helpers/env.ts
|
|
87720
|
+
function parseStringEnv(name21, fallback) {
|
|
87721
|
+
const raw = process.env[name21];
|
|
87722
|
+
return raw === void 0 || raw === "" ? fallback : raw;
|
|
87723
|
+
}
|
|
87724
|
+
function parseUrlEnv(name21, fallback) {
|
|
87725
|
+
return parseStringEnv(name21, fallback).replace(/\/+$/, "");
|
|
87726
|
+
}
|
|
87723
87727
|
function parsePositiveIntEnv(name21, fallback) {
|
|
87724
87728
|
const raw = process.env[name21];
|
|
87725
87729
|
if (raw === void 0 || raw === "") return fallback;
|
|
@@ -87729,6 +87733,11 @@ function parsePositiveIntEnv(name21, fallback) {
|
|
|
87729
87733
|
}
|
|
87730
87734
|
return parsed;
|
|
87731
87735
|
}
|
|
87736
|
+
|
|
87737
|
+
// ../common/src/constants.node.ts
|
|
87738
|
+
var MCP_CALL_TOOL_TIMEOUT = parseInt(process.env.MCP_CALL_TOOL_TIMEOUT ?? "60000", 10);
|
|
87739
|
+
var DEFAULT_REQUEST_TIMEOUT = parseInt(process.env.DEFAULT_REQUEST_TIMEOUT ?? "10000", 10);
|
|
87740
|
+
var SCRIPT_ROUTE_TIMEOUT_MS = 31e4;
|
|
87732
87741
|
var DCR_REFRESH_STARTUP_DELAY_MS = parsePositiveIntEnv("DCR_REFRESH_STARTUP_DELAY_MS", 3e4);
|
|
87733
87742
|
var DCR_REFRESH_INTERVAL_MS = parsePositiveIntEnv("DCR_REFRESH_INTERVAL_MS", 60 * 60 * 1e3);
|
|
87734
87743
|
var DCR_REFRESH_STALE_AFTER_MS = parsePositiveIntEnv("DCR_REFRESH_STALE_AFTER_MS", 6 * 60 * 60 * 1e3);
|
|
@@ -87739,6 +87748,41 @@ var DCR_REFRESH_MAX_FAILURE_WINDOW_MS = parsePositiveIntEnv(
|
|
|
87739
87748
|
72 * 60 * 60 * 1e3
|
|
87740
87749
|
);
|
|
87741
87750
|
|
|
87751
|
+
// ../common/src/provisioning.constants.ts
|
|
87752
|
+
var SKILDER_HAT_DEFAULTS = {
|
|
87753
|
+
slug: "skilder",
|
|
87754
|
+
name: "Skilder",
|
|
87755
|
+
/** HatColor enum value (string-compatible). */
|
|
87756
|
+
color: "BLUE",
|
|
87757
|
+
description: "A gentle and smart assistant that knows the Skilder platform inside and out. Helps users create and manage skills, hats, tools, and automations.",
|
|
87758
|
+
instructions: `You are the Skilder Assistant \u2014 a gentle, knowledgeable, and proactive AI helper.
|
|
87759
|
+
|
|
87760
|
+
Your role is to help users get the most out of the Skilder platform. You know every feature, concept, and workflow: hats, skills, MCP servers, tools, runtimes, organizational hierarchy, and the Studio conversation interface.
|
|
87761
|
+
|
|
87762
|
+
Behavior guidelines:
|
|
87763
|
+
- Be welcoming and patient, especially with new users
|
|
87764
|
+
- Explain concepts in plain language before using technical terms
|
|
87765
|
+
- Proactively suggest next steps and quick wins
|
|
87766
|
+
- When asked to build something, use your Skilder tools to do it \u2014 don't just explain how
|
|
87767
|
+
- If you're unsure about the user's intent, ask a clarifying question
|
|
87768
|
+
- Keep responses concise but complete
|
|
87769
|
+
|
|
87770
|
+
You have access to Skilder MCP tools for managing skills, hats, org units, and tool configurations. Use them to take action on behalf of the user when appropriate.
|
|
87771
|
+
|
|
87772
|
+
## Tool honesty (critical)
|
|
87773
|
+
|
|
87774
|
+
- ONLY call tools that are explicitly listed under the "Available Tools" of a skill you have learned. Call them by their exact name.
|
|
87775
|
+
- NEVER invent or guess a tool name, and NEVER invent a script (e.g. a "create_hat.py") that wasn't listed. If a plausible tool isn't in your learned skills, it does not exist for you.
|
|
87776
|
+
- NEVER claim an action succeeded unless the tool actually returned a successful result. If a tool call errors or is unavailable, say so plainly \u2014 do not pretend it worked.
|
|
87777
|
+
- If you need a capability you don't have a tool for, STOP and tell the user the skill is missing that tool, then suggest they either (i) browse the **Library** to add an existing tool/skill, or (ii) configure a new tool/MCP server. Do not work around a missing tool by faking it.`
|
|
87778
|
+
};
|
|
87779
|
+
var SKILDER_SKILL_IDS = [
|
|
87780
|
+
"skilder-skill-architect",
|
|
87781
|
+
"skilder-platform-expert",
|
|
87782
|
+
"skilder-hat-designer",
|
|
87783
|
+
"human-discovery"
|
|
87784
|
+
];
|
|
87785
|
+
|
|
87742
87786
|
// ../../node_modules/.pnpm/bcryptjs@3.0.3/node_modules/bcryptjs/index.js
|
|
87743
87787
|
var nextTick = typeof setImmediate === "function" ? setImmediate : typeof scheduler === "object" && typeof scheduler.postTask === "function" ? scheduler.postTask.bind(scheduler) : setTimeout;
|
|
87744
87788
|
var BASE64_CODE = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
|
|
@@ -110765,6 +110809,1121 @@ function buildStreamTransport(transport) {
|
|
|
110765
110809
|
var CONNECTED_WINDOW_HOURS = 24;
|
|
110766
110810
|
var CONNECTED_WINDOW_MS = CONNECTED_WINDOW_HOURS * 60 * 60 * 1e3;
|
|
110767
110811
|
|
|
110812
|
+
// ../common/src/skill-catalog-meta.ts
|
|
110813
|
+
var SKILDER_META_FEATURED_SKILLS = [
|
|
110814
|
+
{
|
|
110815
|
+
slug: "skilder-skill-architect",
|
|
110816
|
+
name: "Skilder Skill Architect",
|
|
110817
|
+
description: "Craft high-quality Skilder skills: instructions writing, and attaching references/scripts",
|
|
110818
|
+
tags: ["skilder", "meta", "skill-building"],
|
|
110819
|
+
instructions: `Craft high-quality Skilder skills using these principles.
|
|
110820
|
+
|
|
110821
|
+
For multi-step workflows, use the **delegate** static tool which spawns a sub-agent with full Skilder MCP access.
|
|
110822
|
+
|
|
110823
|
+
## Writing Effective Instructions
|
|
110824
|
+
|
|
110825
|
+
- Keep instructions **concise** \u2014 they are revealed on \`learn\` as a quick guide.
|
|
110826
|
+
- Use **progressive disclosure**: link deep-dive content as named reference documents.
|
|
110827
|
+
- Write in **imperative voice**: "Query the database using..." not "The database can be queried..."
|
|
110828
|
+
- Include **concrete examples** for non-obvious parameters or workflows.
|
|
110829
|
+
|
|
110830
|
+
See instructions-guide.md for length guidelines, templates, and common mistakes.
|
|
110831
|
+
|
|
110832
|
+
## Attaching References & Scripts
|
|
110833
|
+
|
|
110834
|
+
- Attach named documents (references) for deep-dive content agents can fetch on demand.
|
|
110835
|
+
- Attach executable scripts for automation the agent can run via \`execute_script\`.
|
|
110836
|
+
- Agents will see learning paths and execution paths automatically in the learn response.
|
|
110837
|
+
|
|
110838
|
+
See related-resources.md for naming conventions and content guidelines.`,
|
|
110839
|
+
related: [
|
|
110840
|
+
{
|
|
110841
|
+
name: "instructions-guide.md",
|
|
110842
|
+
description: "Writing instructions: tone, length, structure, and common mistakes",
|
|
110843
|
+
mimetype: "text/markdown",
|
|
110844
|
+
content: `# Writing Effective Skill Instructions
|
|
110845
|
+
|
|
110846
|
+
## Principles
|
|
110847
|
+
|
|
110848
|
+
**Be concise.** Instructions are shown on \`learn\`. Keep them scannable \u2014 agents read to act, not to study.
|
|
110849
|
+
|
|
110850
|
+
**Imperative voice.** Write "Query the database using the filter parameter" not "The database can be queried using..."
|
|
110851
|
+
|
|
110852
|
+
**Progressive disclosure.** Put deep detail in \`@references/...\` documents.
|
|
110853
|
+
- Instructions = orientation (what this skill does and how to start)
|
|
110854
|
+
- References = manual (edge cases, full field lists, error codes)
|
|
110855
|
+
|
|
110856
|
+
**Give examples.** When a parameter or workflow is non-obvious, include a one-liner example inline.
|
|
110857
|
+
|
|
110858
|
+
---
|
|
110859
|
+
|
|
110860
|
+
## Recommended Structure
|
|
110861
|
+
|
|
110862
|
+
\`\`\`markdown
|
|
110863
|
+
Brief description of what this skill does and when to use it.
|
|
110864
|
+
|
|
110865
|
+
## Core Concepts
|
|
110866
|
+
- Key term 1: brief definition
|
|
110867
|
+
- Key term 2: brief definition
|
|
110868
|
+
|
|
110869
|
+
## How To Use
|
|
110870
|
+
1. First step with example
|
|
110871
|
+
2. Second step
|
|
110872
|
+
3. Third step
|
|
110873
|
+
|
|
110874
|
+
## Important Constraints
|
|
110875
|
+
- Limitation or gotcha 1
|
|
110876
|
+
- Limitation or gotcha 2
|
|
110877
|
+
|
|
110878
|
+
For deeper guidance, see:
|
|
110879
|
+
- @references/doc-name.md \u2014 what it covers
|
|
110880
|
+
\`\`\`
|
|
110881
|
+
|
|
110882
|
+
---
|
|
110883
|
+
|
|
110884
|
+
## Length Guidelines
|
|
110885
|
+
|
|
110886
|
+
| Skill complexity | Instructions length |
|
|
110887
|
+
|-------------------------|---------------------|
|
|
110888
|
+
| Simple (1\u20133 tools) | 50\u2013150 words |
|
|
110889
|
+
| Moderate (4\u20138 tools) | 150\u2013300 words |
|
|
110890
|
+
| Complex (workflow) | 200\u2013400 words |
|
|
110891
|
+
|
|
110892
|
+
---
|
|
110893
|
+
|
|
110894
|
+
## Common Mistakes
|
|
110895
|
+
|
|
110896
|
+
| Mistake | Fix |
|
|
110897
|
+
|---|---|
|
|
110898
|
+
| Listing all tool parameters inline | Move to a reference document |
|
|
110899
|
+
| "Use the tools as needed" (too vague) | Give concrete step-by-step guidance |
|
|
110900
|
+
| No examples for ambiguous inputs | Add a one-liner example per non-obvious param |
|
|
110901
|
+
| Missing constraints | State rate limits, permissions, error conditions |
|
|
110902
|
+
| Instructions longer than 500 words | Split into references |
|
|
110903
|
+
`
|
|
110904
|
+
},
|
|
110905
|
+
{
|
|
110906
|
+
name: "related-resources.md",
|
|
110907
|
+
description: "How to attach, name, and document references and scripts",
|
|
110908
|
+
mimetype: "text/markdown",
|
|
110909
|
+
content: `# Attaching References and Scripts
|
|
110910
|
+
|
|
110911
|
+
## References
|
|
110912
|
+
|
|
110913
|
+
References are named markdown documents attached to a skill. They appear in the learn response as paths the agent can fetch on demand.
|
|
110914
|
+
|
|
110915
|
+
### Creating a Reference
|
|
110916
|
+
|
|
110917
|
+
In the Studio UI: **Skill \u2192 References tab \u2192 Add Reference**
|
|
110918
|
+
Via Skilder MCP server: \`create_skill_reference\` tool
|
|
110919
|
+
|
|
110920
|
+
### Naming Conventions
|
|
110921
|
+
|
|
110922
|
+
Use descriptive, hyphenated names with \`.md\` extension:
|
|
110923
|
+
- \`api-guide.md\` \u2014 REST endpoint documentation
|
|
110924
|
+
- \`field-mapping.md\` \u2014 Input/output field mappings
|
|
110925
|
+
- \`error-codes.md\` \u2014 Error code reference
|
|
110926
|
+
- \`examples.md\` \u2014 Request/response examples
|
|
110927
|
+
|
|
110928
|
+
### Mentioning in Instructions
|
|
110929
|
+
|
|
110930
|
+
Use \`@references/filename.md\` anywhere in the instructions text. The learn response automatically generates the learning path:
|
|
110931
|
+
|
|
110932
|
+
\`\`\`
|
|
110933
|
+
learn --path "/Skill Name/references/filename.md"
|
|
110934
|
+
\`\`\`
|
|
110935
|
+
|
|
110936
|
+
### Content Guidelines
|
|
110937
|
+
|
|
110938
|
+
- References should be **self-contained** documents
|
|
110939
|
+
- Use headers, tables, and code blocks freely
|
|
110940
|
+
- Include examples, not just definitions
|
|
110941
|
+
- References can be updated independently of skill instructions
|
|
110942
|
+
|
|
110943
|
+
---
|
|
110944
|
+
|
|
110945
|
+
## Scripts
|
|
110946
|
+
|
|
110947
|
+
Scripts are executable code attached to a skill. They run on the linked edge runtime.
|
|
110948
|
+
|
|
110949
|
+
### Supported Types
|
|
110950
|
+
|
|
110951
|
+
- **Python** \u2014 \`.py\` files
|
|
110952
|
+
- **Bash** \u2014 \`.sh\` files (on compatible runtimes)
|
|
110953
|
+
|
|
110954
|
+
### Mentioning in Instructions
|
|
110955
|
+
|
|
110956
|
+
Use \`@scripts/filename.py\` in instructions. The learn response shows the execution path:
|
|
110957
|
+
|
|
110958
|
+
\`\`\`
|
|
110959
|
+
execute_script --path "/Skill Name/scripts/filename.py --arg=value"
|
|
110960
|
+
\`\`\`
|
|
110961
|
+
|
|
110962
|
+
### Best Practices
|
|
110963
|
+
|
|
110964
|
+
- Add a \`--help\` argument to scripts for self-documentation
|
|
110965
|
+
- Use descriptive argument names
|
|
110966
|
+
- Keep scripts focused \u2014 one script per distinct operation
|
|
110967
|
+
- Document expected inputs and outputs in the script header comment
|
|
110968
|
+
`
|
|
110969
|
+
}
|
|
110970
|
+
]
|
|
110971
|
+
},
|
|
110972
|
+
{
|
|
110973
|
+
slug: "skilder-platform-expert",
|
|
110974
|
+
name: "Skilder Platform Expert",
|
|
110975
|
+
description: "Full conceptual guide: entities, relationships, constraints, and common workflows for managing a Skilder workspace",
|
|
110976
|
+
tags: ["skilder", "meta", "platform"],
|
|
110977
|
+
instructions: `Master the Skilder platform entities and their relationships.
|
|
110978
|
+
|
|
110979
|
+
## Core Entities
|
|
110980
|
+
|
|
110981
|
+
- **Workspace** \u2014 Tenant boundary. All resources (hats, skills, runtimes) are workspace-scoped.
|
|
110982
|
+
- **Hat** \u2014 A named, color-coded collection of skills. Represents a role or persona for an agent.
|
|
110983
|
+
- **Skill** \u2014 A unit of capability: instructions + MCP tools + references + scripts.
|
|
110984
|
+
- **MCP Tool** \u2014 A callable tool exposed by an external MCP server, linked to a skill.
|
|
110985
|
+
- **Reference** \u2014 A named markdown document attached to a skill for contextual knowledge.
|
|
110986
|
+
- **Script** \u2014 Executable code (Python, Bash) attached to a skill, run via \`execute_script\`.
|
|
110987
|
+
- **Runtime** \u2014 An edge runtime instance that connects external MCP servers to Skilder.
|
|
110988
|
+
|
|
110989
|
+
## Key Relationships
|
|
110990
|
+
|
|
110991
|
+
\`\`\`
|
|
110992
|
+
Workspace
|
|
110993
|
+
\u2514\u2500\u2500 Hats \u2192 Skills \u2192 MCP Tools (served by Runtimes)
|
|
110994
|
+
\u2514\u2500\u2500 References (markdown knowledge)
|
|
110995
|
+
\u2514\u2500\u2500 Scripts (executable automation)
|
|
110996
|
+
\`\`\`
|
|
110997
|
+
|
|
110998
|
+
## Common Workflows
|
|
110999
|
+
|
|
111000
|
+
1. **Add a skill**: Create skill \u2192 attach MCP tools from a runtime \u2192 add references \u2192 assign to a hat
|
|
111001
|
+
2. **Agent connects**: \`init_skilder\` \u2192 \`learn /hats/Hat Name\` or \`learn /Skill Name\` \u2192 use unlocked tools
|
|
111002
|
+
3. **Import from GitHub**: Use the GitHub import to pull SKILL.md + references from a repo
|
|
111003
|
+
|
|
111004
|
+
For deeper guidance, see:
|
|
111005
|
+
- entities.md \u2014 full entity reference with all fields and constraints
|
|
111006
|
+
- workflows.md \u2014 step-by-step common workflows with the Skilder MCP server
|
|
111007
|
+
- mcp-tools.md \u2014 how MCP tools are registered, routed, and exposed to agents`,
|
|
111008
|
+
related: [
|
|
111009
|
+
{
|
|
111010
|
+
name: "entities.md",
|
|
111011
|
+
description: "Full entity reference with all fields and constraints",
|
|
111012
|
+
mimetype: "text/markdown",
|
|
111013
|
+
content: `# Skilder Entity Reference
|
|
111014
|
+
|
|
111015
|
+
## Workspace
|
|
111016
|
+
|
|
111017
|
+
The top-level organizational boundary. Every resource belongs to exactly one workspace.
|
|
111018
|
+
|
|
111019
|
+
| Field | Type | Notes |
|
|
111020
|
+
|---|---|---|
|
|
111021
|
+
| id | ID | Immutable |
|
|
111022
|
+
| name | String | Display name |
|
|
111023
|
+
| slug | String | URL-safe identifier |
|
|
111024
|
+
| members | [User] | Workspace members with roles |
|
|
111025
|
+
|
|
111026
|
+
**Constraints:** Skills, hats, and runtimes cannot be shared across workspaces.
|
|
111027
|
+
|
|
111028
|
+
---
|
|
111029
|
+
|
|
111030
|
+
## Hat
|
|
111031
|
+
|
|
111032
|
+
A named, color-coded collection of skills representing a role.
|
|
111033
|
+
|
|
111034
|
+
| Field | Type | Notes |
|
|
111035
|
+
|---|---|---|
|
|
111036
|
+
| id | ID | Immutable |
|
|
111037
|
+
| name | String | Role name shown to agents |
|
|
111038
|
+
| description | String | Brief role description (shown in init_skilder) |
|
|
111039
|
+
| instructions | String | Full hat-level guidance revealed on learn |
|
|
111040
|
+
| color | String | Hex color (e.g., \`#3B82F6\`) |
|
|
111041
|
+
| skills | [Skill] | Ordered list of skills in this hat |
|
|
111042
|
+
| disabled | Boolean | If true, hidden from agents |
|
|
111043
|
+
|
|
111044
|
+
**Constraints:** A skill can belong to multiple hats. A hat must have at least one enabled skill to appear in \`init_skilder\`.
|
|
111045
|
+
|
|
111046
|
+
---
|
|
111047
|
+
|
|
111048
|
+
## Skill
|
|
111049
|
+
|
|
111050
|
+
A unit of capability: instructions, tools, references, and scripts.
|
|
111051
|
+
|
|
111052
|
+
| Field | Type | Notes |
|
|
111053
|
+
|---|---|---|
|
|
111054
|
+
| id | ID | Immutable |
|
|
111055
|
+
| name | String | Used as the learn path segment (e.g., \`learn "/My Skill"\`) |
|
|
111056
|
+
| description | String | \u2264 500 chars; shown before the agent learns the skill |
|
|
111057
|
+
| instructions | String | Full instructions revealed on \`learn\` |
|
|
111058
|
+
| mcpTools | [MCPTool] | Tools exposed when skill is learned |
|
|
111059
|
+
| references | [SkillReference] | Named knowledge documents |
|
|
111060
|
+
| scripts | [SkillScript] | Executable scripts |
|
|
111061
|
+
| disabled | Boolean | If true, hidden from agents and the catalog |
|
|
111062
|
+
|
|
111063
|
+
**Constraints:** Skill names must be unique within a workspace. Description is capped at 500 characters.
|
|
111064
|
+
|
|
111065
|
+
---
|
|
111066
|
+
|
|
111067
|
+
## MCP Tool
|
|
111068
|
+
|
|
111069
|
+
An individual callable tool from an external MCP server.
|
|
111070
|
+
|
|
111071
|
+
| Field | Type | Notes |
|
|
111072
|
+
|---|---|---|
|
|
111073
|
+
| id | ID | Immutable |
|
|
111074
|
+
| name | String | Tool name as registered in the MCP server |
|
|
111075
|
+
| description | String | Shown in the agent's tool list |
|
|
111076
|
+
| inputSchema | JSON | JSON Schema for tool arguments |
|
|
111077
|
+
| mcpServer | MCPServer | The server that provides this tool |
|
|
111078
|
+
|
|
111079
|
+
**Constraints:** Tool names must be globally unique within a workspace (they are merged into a single tool list per agent session).
|
|
111080
|
+
|
|
111081
|
+
---
|
|
111082
|
+
|
|
111083
|
+
## Skill Reference
|
|
111084
|
+
|
|
111085
|
+
A named markdown document attached to a skill.
|
|
111086
|
+
|
|
111087
|
+
| Field | Type | Notes |
|
|
111088
|
+
|---|---|---|
|
|
111089
|
+
| id | ID | Immutable |
|
|
111090
|
+
| name | String | Filename used in the learn path (e.g., \`api-guide.md\`) |
|
|
111091
|
+
| description | String | Brief description shown in the References section |
|
|
111092
|
+
| instructions | String | Full markdown content of the document |
|
|
111093
|
+
|
|
111094
|
+
**Access path:** \`learn "/Skill Name/references/filename.md"\`
|
|
111095
|
+
|
|
111096
|
+
---
|
|
111097
|
+
|
|
111098
|
+
## Skill Script
|
|
111099
|
+
|
|
111100
|
+
Executable code attached to a skill.
|
|
111101
|
+
|
|
111102
|
+
| Field | Type | Notes |
|
|
111103
|
+
|---|---|---|
|
|
111104
|
+
| id | ID | Immutable |
|
|
111105
|
+
| name | String | Filename used in the execute path (e.g., \`transform.py\`) |
|
|
111106
|
+
| type | ScriptType | PYTHON | BASH |
|
|
111107
|
+
| code | String | Script source code |
|
|
111108
|
+
| runtime | Runtime | The edge runtime that will execute the script |
|
|
111109
|
+
|
|
111110
|
+
**Execution path:** \`execute_script "/Skill Name/scripts/filename.py --arg=value"\`
|
|
111111
|
+
|
|
111112
|
+
---
|
|
111113
|
+
|
|
111114
|
+
## Runtime
|
|
111115
|
+
|
|
111116
|
+
An edge runtime instance that connects external MCP servers to Skilder.
|
|
111117
|
+
|
|
111118
|
+
| Field | Type | Notes |
|
|
111119
|
+
|---|---|---|
|
|
111120
|
+
| id | ID | Immutable |
|
|
111121
|
+
| name | String | Display name |
|
|
111122
|
+
| runtimeId | String | Unique runtime identifier for NATS routing |
|
|
111123
|
+
| mcpServers | [MCPServer] | MCP servers managed by this runtime |
|
|
111124
|
+
|
|
111125
|
+
**Notes:** Scripts are linked to a specific runtime. MCP tools are linked to an MCP server, which belongs to a runtime.
|
|
111126
|
+
`
|
|
111127
|
+
},
|
|
111128
|
+
{
|
|
111129
|
+
name: "workflows.md",
|
|
111130
|
+
description: "Step-by-step common workflows with the Skilder MCP server",
|
|
111131
|
+
mimetype: "text/markdown",
|
|
111132
|
+
content: `# Common Skilder Workflows
|
|
111133
|
+
|
|
111134
|
+
These workflows use the Skilder MCP server tools available after connecting.
|
|
111135
|
+
|
|
111136
|
+
---
|
|
111137
|
+
|
|
111138
|
+
## Create a Skill
|
|
111139
|
+
|
|
111140
|
+
1. **Create the skill** using \`create_skill\` with name and description.
|
|
111141
|
+
2. **Add instructions** via \`update_skill\` \u2014 keep them concise (see Skilder Skill Architect skill).
|
|
111142
|
+
3. **Attach MCP tools** using \`add_mcp_tool_to_skill\` \u2014 tools must already exist on a runtime.
|
|
111143
|
+
4. **Add references** using \`create_skill_reference\` for each knowledge document.
|
|
111144
|
+
5. **Assign to a hat** using \`add_skill_to_hat\` \u2014 or create a new hat first.
|
|
111145
|
+
|
|
111146
|
+
---
|
|
111147
|
+
|
|
111148
|
+
## Create a Hat
|
|
111149
|
+
|
|
111150
|
+
1. **Create the hat** using \`create_hat\` with name, description, and color.
|
|
111151
|
+
2. **Add skills** using \`add_skill_to_hat\` \u2014 skills must already exist.
|
|
111152
|
+
3. **Set hat instructions** (optional) via \`update_hat\` for hat-level agent guidance.
|
|
111153
|
+
|
|
111154
|
+
---
|
|
111155
|
+
|
|
111156
|
+
## Import a Skill from GitHub
|
|
111157
|
+
|
|
111158
|
+
Use \`import_skill_from_github\` with the repository URL and path to the SKILL.md file.
|
|
111159
|
+
|
|
111160
|
+
The import reads:
|
|
111161
|
+
- \`SKILL.md\` \u2014 skill metadata (name, description) and instructions
|
|
111162
|
+
- \`references/\` directory \u2014 all \`.md\` files become skill references
|
|
111163
|
+
- \`scripts/\` directory \u2014 all script files become skill scripts (linked to a specified runtime)
|
|
111164
|
+
|
|
111165
|
+
---
|
|
111166
|
+
|
|
111167
|
+
## Update a Skill's Instructions
|
|
111168
|
+
|
|
111169
|
+
Use \`update_skill\` with the \`instructions\` field. Changes take effect on the next agent session (the catalog is rebuilt on each user handshake).
|
|
111170
|
+
|
|
111171
|
+
---
|
|
111172
|
+
|
|
111173
|
+
## Disable a Skill or Hat
|
|
111174
|
+
|
|
111175
|
+
Use \`update_skill\` or \`update_hat\` with \`disabled: true\`. The resource is hidden from agents immediately (the catalog is refreshed on next connection).
|
|
111176
|
+
|
|
111177
|
+
---
|
|
111178
|
+
|
|
111179
|
+
## Agent Session Lifecycle
|
|
111180
|
+
|
|
111181
|
+
1. Agent connects to the Skilder runtime (via MCP server URL).
|
|
111182
|
+
2. Runtime receives the workspace catalog from the backend via NATS.
|
|
111183
|
+
3. Agent calls \`init_skilder\` \u2014 sees available hats and skills (name + description only).
|
|
111184
|
+
4. Agent calls \`learn "/hats/Hat Name"\` or \`learn "/Skill Name"\` \u2014 receives full instructions + tool list.
|
|
111185
|
+
5. Newly unlocked MCP tools appear in the agent's tool list immediately.
|
|
111186
|
+
6. Agent calls \`execute_script\` to run attached scripts on the edge runtime.
|
|
111187
|
+
`
|
|
111188
|
+
},
|
|
111189
|
+
{
|
|
111190
|
+
name: "mcp-tools.md",
|
|
111191
|
+
description: "How MCP tools are registered, routed, and exposed to agents",
|
|
111192
|
+
mimetype: "text/markdown",
|
|
111193
|
+
content: `# MCP Tool Registration and Routing
|
|
111194
|
+
|
|
111195
|
+
## Overview
|
|
111196
|
+
|
|
111197
|
+
MCP tools in Skilder are not defined directly \u2014 they are **discovered** from external MCP servers running on edge runtimes. The flow is:
|
|
111198
|
+
|
|
111199
|
+
\`\`\`
|
|
111200
|
+
External MCP Server \u2192 Runtime (discovers tools) \u2192 Skilder (stores MCPTool nodes) \u2192 Skill (attaches tools) \u2192 Agent (uses tools)
|
|
111201
|
+
\`\`\`
|
|
111202
|
+
|
|
111203
|
+
---
|
|
111204
|
+
|
|
111205
|
+
## Registration
|
|
111206
|
+
|
|
111207
|
+
When an edge runtime starts, it:
|
|
111208
|
+
1. Spawns or connects to the external MCP servers configured for that runtime.
|
|
111209
|
+
2. Calls \`tools/list\` on each MCP server.
|
|
111210
|
+
3. Registers the discovered tools as \`MCPTool\` nodes in Skilder (linked to the runtime's MCP server).
|
|
111211
|
+
|
|
111212
|
+
Tools are re-registered on runtime restart. If a tool's input schema changes, the stored node is updated.
|
|
111213
|
+
|
|
111214
|
+
---
|
|
111215
|
+
|
|
111216
|
+
## Routing
|
|
111217
|
+
|
|
111218
|
+
When an agent calls an MCP tool:
|
|
111219
|
+
|
|
111220
|
+
1. The user runtime looks up the tool's routing info (\`skillId\`, \`runtimeId\`).
|
|
111221
|
+
2. It publishes a \`SkillCallToolRequest\` to NATS subject: \`{workspaceId}.call-tool.{toolId}.{runtimeId}\`
|
|
111222
|
+
3. The target edge runtime receives the request and forwards it to the appropriate MCP server.
|
|
111223
|
+
4. The response is returned via NATS and forwarded to the agent.
|
|
111224
|
+
|
|
111225
|
+
**Timeout:** Default tool call timeout is configurable; long-running operations should use scripts instead.
|
|
111226
|
+
|
|
111227
|
+
---
|
|
111228
|
+
|
|
111229
|
+
## Tool Name Uniqueness
|
|
111230
|
+
|
|
111231
|
+
Tool names must be **globally unique** within a workspace. If two MCP servers provide a tool with the same name, only one can be linked to a skill. Use the MCP server's tool name prefix feature to disambiguate.
|
|
111232
|
+
|
|
111233
|
+
---
|
|
111234
|
+
|
|
111235
|
+
## Progressive Exposure
|
|
111236
|
+
|
|
111237
|
+
Tools are not visible to the agent until the skill they belong to is learned. This keeps the agent's tool list focused on what is relevant for the current task.
|
|
111238
|
+
|
|
111239
|
+
- After \`init_skilder\`: only static tools are visible (\`init_skilder\`, \`learn\`, \`execute_script\`, \`feedback_skill\`).
|
|
111240
|
+
- After \`learn "/Skill Name"\`: all tools linked to that skill are added to the tool list.
|
|
111241
|
+
- After \`learn "/hats/Hat Name"\`: all tools from all skills in that hat are added.
|
|
111242
|
+
`
|
|
111243
|
+
}
|
|
111244
|
+
]
|
|
111245
|
+
},
|
|
111246
|
+
{
|
|
111247
|
+
slug: "skilder-hat-designer",
|
|
111248
|
+
name: "Skilder Hat Designer",
|
|
111249
|
+
description: "Design role-based hat collections: org unit mapping, skill selection, and color conventions",
|
|
111250
|
+
tags: ["skilder", "meta", "hats"],
|
|
111251
|
+
instructions: `Design effective hat collections that give agents focused, role-appropriate context.
|
|
111252
|
+
|
|
111253
|
+
## What Is a Hat?
|
|
111254
|
+
|
|
111255
|
+
A hat is a named role a workspace assigns to an agent. It bundles a set of skills the agent needs for that role, using a color for quick visual identification in the Studio UI.
|
|
111256
|
+
|
|
111257
|
+
## Design Principles
|
|
111258
|
+
|
|
111259
|
+
- **One hat per role** \u2014 Align hats with job functions (e.g., "Customer Support Specialist", "Finance Analyst"), not tasks.
|
|
111260
|
+
- **Minimal skill sets** \u2014 Include only skills the role actually needs. Smaller hats mean faster, more focused agents.
|
|
111261
|
+
- **Clear names** \u2014 Use role names agents can recognize: "Senior DevOps Engineer" not "Hat 1".
|
|
111262
|
+
- **Hat-level instructions** \u2014 Use the hat's \`instructions\` field for role-level context that applies to all skills in the hat.
|
|
111263
|
+
|
|
111264
|
+
## Org Unit Mapping
|
|
111265
|
+
|
|
111266
|
+
Map hats to organizational units (teams, departments, functions):
|
|
111267
|
+
- A hat can be shared across multiple org units if roles overlap.
|
|
111268
|
+
- Create separate hats for roles with meaningfully different skill sets, even within the same team.
|
|
111269
|
+
- Avoid creating redundant hats for minor variations \u2014 use skill \`instructions\` differences instead.
|
|
111270
|
+
|
|
111271
|
+
## Color Conventions
|
|
111272
|
+
|
|
111273
|
+
Colors should reflect the function or urgency of the role. See color-palette.md for exact hex codes.
|
|
111274
|
+
|
|
111275
|
+
| Role type | Color family | Example |
|
|
111276
|
+
|---|---|---|
|
|
111277
|
+
| Technical / Engineering | Blue | \`#3B82F6\` |
|
|
111278
|
+
| Operations / Support | Green | \`#10B981\` |
|
|
111279
|
+
| Customer-facing / Sales | Orange | \`#F59E0B\` |
|
|
111280
|
+
| Admin / Governance | Red | \`#EF4444\` |
|
|
111281
|
+
| Creative / Strategy | Purple | \`#8B5CF6\` |
|
|
111282
|
+
| Data / Analytics | Teal | \`#14B8A6\` |
|
|
111283
|
+
|
|
111284
|
+
For a complete hat design guide with examples and anti-patterns, see hat-design-guide.md.`,
|
|
111285
|
+
related: [
|
|
111286
|
+
{
|
|
111287
|
+
name: "hat-design-guide.md",
|
|
111288
|
+
description: "Complete hat design guide with examples, anti-patterns, and org mapping templates",
|
|
111289
|
+
mimetype: "text/markdown",
|
|
111290
|
+
content: `# Hat Design Guide
|
|
111291
|
+
|
|
111292
|
+
## Anatomy of a Well-Designed Hat
|
|
111293
|
+
|
|
111294
|
+
A good hat has:
|
|
111295
|
+
- **A focused role name** that an agent can understand without ambiguity
|
|
111296
|
+
- **A concise description** (1\u20132 sentences) explaining the role's purpose
|
|
111297
|
+
- **Hat-level instructions** for context that applies across all skills in the hat (e.g., tone, constraints, escalation rules)
|
|
111298
|
+
- **3\u20138 skills** \u2014 enough to cover the role's needs without overwhelming the agent
|
|
111299
|
+
- **A consistent color** that matches the role's function
|
|
111300
|
+
|
|
111301
|
+
---
|
|
111302
|
+
|
|
111303
|
+
## Examples
|
|
111304
|
+
|
|
111305
|
+
### Good Hat Design: "Customer Support Tier 1"
|
|
111306
|
+
|
|
111307
|
+
\`\`\`
|
|
111308
|
+
Name: Customer Support Tier 1
|
|
111309
|
+
Description: Handle inbound customer inquiries, ticket triage, and FAQ resolution
|
|
111310
|
+
Color: #F59E0B (orange \u2014 customer-facing)
|
|
111311
|
+
Skills:
|
|
111312
|
+
- Ticket Management (create, update, close tickets)
|
|
111313
|
+
- Knowledge Base Search (large article set)
|
|
111314
|
+
- Customer Profile Lookup (read-only CRM access)
|
|
111315
|
+
Instructions: "Always greet customers by first name. Escalate to Tier 2 if resolution
|
|
111316
|
+
requires more than 3 tool calls. Log all interactions."
|
|
111317
|
+
\`\`\`
|
|
111318
|
+
|
|
111319
|
+
### Anti-Pattern: "Everything Hat"
|
|
111320
|
+
|
|
111321
|
+
\`\`\`
|
|
111322
|
+
Name: General Assistant
|
|
111323
|
+
Skills: (20 skills covering billing, HR, DevOps, marketing, legal...)
|
|
111324
|
+
\`\`\`
|
|
111325
|
+
|
|
111326
|
+
**Problem:** The agent sees every tool from every skill after learning the hat. This floods the tool list and dilutes focus.
|
|
111327
|
+
|
|
111328
|
+
**Fix:** Split into role-specific hats. Use \`learn "/Skill Name"\` for ad-hoc access to individual skills.
|
|
111329
|
+
|
|
111330
|
+
---
|
|
111331
|
+
|
|
111332
|
+
## Skill Selection Checklist
|
|
111333
|
+
|
|
111334
|
+
For each skill you consider adding to a hat, ask:
|
|
111335
|
+
- [ ] Does this role need this capability in their typical workflow?
|
|
111336
|
+
- [ ] Would an agent in this role be authorized to use these tools?
|
|
111337
|
+
- [ ] Does this skill's instructions align with this role's context?
|
|
111338
|
+
- [ ] Are there fewer than 10 skills in the hat already?
|
|
111339
|
+
|
|
111340
|
+
---
|
|
111341
|
+
|
|
111342
|
+
## Org Unit Mapping Template
|
|
111343
|
+
|
|
111344
|
+
| Org Unit | Primary Hat | Secondary Hat (optional) |
|
|
111345
|
+
|---|---|---|
|
|
111346
|
+
| Engineering | "Software Engineer" | "DevOps Operator" |
|
|
111347
|
+
| Finance | "Finance Analyst" | "Finance Admin" |
|
|
111348
|
+
| HR | "HR Business Partner" | \u2014 |
|
|
111349
|
+
| Customer Support | "CS Tier 1" | "CS Tier 2" |
|
|
111350
|
+
| Legal | "Legal Reviewer" | \u2014 |
|
|
111351
|
+
|
|
111352
|
+
**Note:** Agents typically wear one hat per session. Design hats for the most common single-session workflow, not for covering every possible task.
|
|
111353
|
+
|
|
111354
|
+
---
|
|
111355
|
+
|
|
111356
|
+
## Hat Instructions Best Practices
|
|
111357
|
+
|
|
111358
|
+
Hat-level \`instructions\` appear when an agent learns the hat (before individual skill instructions). Use them for:
|
|
111359
|
+
- Role identity framing ("You are a Senior Support Specialist...")
|
|
111360
|
+
- Cross-skill constraints ("Always check the customer's entitlement before using billing tools")
|
|
111361
|
+
- Escalation rules ("If unsure, prefer learn over act")
|
|
111362
|
+
- Tone and communication style guidance
|
|
111363
|
+
|
|
111364
|
+
Keep hat instructions under 200 words \u2014 they are shown alongside each skill's instructions, so context adds up quickly.
|
|
111365
|
+
`
|
|
111366
|
+
},
|
|
111367
|
+
{
|
|
111368
|
+
name: "color-palette.md",
|
|
111369
|
+
description: "Exact color codes and usage conventions for each role type",
|
|
111370
|
+
mimetype: "text/markdown",
|
|
111371
|
+
content: `# Hat Color Palette
|
|
111372
|
+
|
|
111373
|
+
Colors are stored as hex strings (e.g., \`#3B82F6\`). Use the role-type conventions below for consistency across your workspace.
|
|
111374
|
+
|
|
111375
|
+
---
|
|
111376
|
+
|
|
111377
|
+
## Technical / Engineering
|
|
111378
|
+
|
|
111379
|
+
| Shade | Hex | Usage |
|
|
111380
|
+
|---|---|---|
|
|
111381
|
+
| Blue 500 | \`#3B82F6\` | General engineering roles |
|
|
111382
|
+
| Blue 700 | \`#1D4ED8\` | Senior / lead engineering roles |
|
|
111383
|
+
| Indigo 500 | \`#6366F1\` | Platform / infrastructure roles |
|
|
111384
|
+
| Cyan 500 | \`#06B6D4\` | Backend / API roles |
|
|
111385
|
+
|
|
111386
|
+
---
|
|
111387
|
+
|
|
111388
|
+
## Operations / Support
|
|
111389
|
+
|
|
111390
|
+
| Shade | Hex | Usage |
|
|
111391
|
+
|---|---|---|
|
|
111392
|
+
| Green 500 | \`#10B981\` | General operations |
|
|
111393
|
+
| Green 700 | \`#047857\` | Operations leads |
|
|
111394
|
+
| Lime 500 | \`#84CC16\` | IT support |
|
|
111395
|
+
| Emerald 500 | \`#10B981\` | Service reliability |
|
|
111396
|
+
|
|
111397
|
+
---
|
|
111398
|
+
|
|
111399
|
+
## Customer-Facing / Sales
|
|
111400
|
+
|
|
111401
|
+
| Shade | Hex | Usage |
|
|
111402
|
+
|---|---|---|
|
|
111403
|
+
| Amber 500 | \`#F59E0B\` | Customer support |
|
|
111404
|
+
| Orange 500 | \`#F97316\` | Sales / Account management |
|
|
111405
|
+
| Yellow 500 | \`#EAB308\` | Marketing |
|
|
111406
|
+
|
|
111407
|
+
---
|
|
111408
|
+
|
|
111409
|
+
## Admin / Governance
|
|
111410
|
+
|
|
111411
|
+
| Shade | Hex | Usage |
|
|
111412
|
+
|---|---|---|
|
|
111413
|
+
| Red 500 | \`#EF4444\` | Admin roles |
|
|
111414
|
+
| Rose 500 | \`#F43F5E\` | Compliance / Legal |
|
|
111415
|
+
| Red 700 | \`#B91C1C\` | Security / Audit |
|
|
111416
|
+
|
|
111417
|
+
---
|
|
111418
|
+
|
|
111419
|
+
## Creative / Strategy
|
|
111420
|
+
|
|
111421
|
+
| Shade | Hex | Usage |
|
|
111422
|
+
|---|---|---|
|
|
111423
|
+
| Purple 500 | \`#8B5CF6\` | Product / Strategy |
|
|
111424
|
+
| Violet 500 | \`#7C3AED\` | Design / UX |
|
|
111425
|
+
| Fuchsia 500 | \`#D946EF\` | Creative / Content |
|
|
111426
|
+
|
|
111427
|
+
---
|
|
111428
|
+
|
|
111429
|
+
## Data / Analytics
|
|
111430
|
+
|
|
111431
|
+
| Shade | Hex | Usage |
|
|
111432
|
+
|---|---|---|
|
|
111433
|
+
| Teal 500 | \`#14B8A6\` | Data analysts |
|
|
111434
|
+
| Sky 500 | \`#0EA5E9\` | Data engineers |
|
|
111435
|
+
| Slate 500 | \`#64748B\` | Reporting / BI |
|
|
111436
|
+
|
|
111437
|
+
---
|
|
111438
|
+
|
|
111439
|
+
## Neutral / Cross-Functional
|
|
111440
|
+
|
|
111441
|
+
| Shade | Hex | Usage |
|
|
111442
|
+
|---|---|---|
|
|
111443
|
+
| Gray 500 | \`#6B7280\` | Cross-functional or temporary roles |
|
|
111444
|
+
| Stone 500 | \`#78716C\` | General purpose / sandbox |
|
|
111445
|
+
|
|
111446
|
+
---
|
|
111447
|
+
|
|
111448
|
+
## Tips
|
|
111449
|
+
|
|
111450
|
+
- Use **darker shades** for senior/lead variants of a role.
|
|
111451
|
+
- Keep color assignments **consistent** across your workspace \u2014 the same role type should always use the same color family.
|
|
111452
|
+
- Avoid using the same hex for two different hats in the same workspace \u2014 agents and users will confuse them.
|
|
111453
|
+
`
|
|
111454
|
+
}
|
|
111455
|
+
]
|
|
111456
|
+
}
|
|
111457
|
+
];
|
|
111458
|
+
|
|
111459
|
+
// ../common/src/skill-catalog-onboarding.ts
|
|
111460
|
+
var ONBOARDING_FEATURED_SKILLS = [
|
|
111461
|
+
{
|
|
111462
|
+
slug: "human-discovery",
|
|
111463
|
+
name: "Human Discovery",
|
|
111464
|
+
description: `Use this skill to help a person discover what AI skill would be most useful for their job. Trigger whenever someone says things like "I don't know where to start with AI", "what skill should I create", "help me find a use case", "I'm not technical but want to use AI", "suggest a skill for my job", or anything that implies they want AI help but don't know how. Walk them through a guided discovery process: understand their job in 4 questions or fewer, suggest 3 pain points addressable by a Claude skill (no heavy integrations), let them pick one, interview them briefly, then write the skill together.`,
|
|
111465
|
+
tags: ["discovery", "onboarding", "first-time-user"],
|
|
111466
|
+
instructions: `# Skill Discovery for first time user \u2014 Find the Right AI Skill for Any Job
|
|
111467
|
+
|
|
111468
|
+
You are helping a non-technical person discover which AI skill would make the biggest difference in their work. Your tone should be **warm, curious, and jargon-free**. Never use terms like "LLM", "prompt", "API", "workflow automation". Speak like a friendly colleague, not a consultant. Apply ALWAYS if the user say he is new, first time using, don't know what to do with AI.
|
|
111469
|
+
|
|
111470
|
+
---
|
|
111471
|
+
|
|
111472
|
+
## Phase 1: Discover the Human (max 4 questions)
|
|
111473
|
+
|
|
111474
|
+
Your goal is to build a clear picture of who this person is and where they struggle \u2014 in 4 questions or fewer. Use \`ask_user_input_v0\` for every question. **Do not ask all 4 at once.** Ask one (or at most two related ones) at a time, let their answer guide what you ask next.
|
|
111475
|
+
|
|
111476
|
+
### Question arc
|
|
111477
|
+
|
|
111478
|
+
**Q1 \u2014 Role/Activity** (free text, always first)
|
|
111479
|
+
|
|
111480
|
+
> "What's your job, or what do you mainly spend your time doing?"
|
|
111481
|
+
|
|
111482
|
+
**Q2 \u2014 Hierarchy position** (single select) Options: Individual contributor / Team lead or manager / Senior leader or executive / Freelancer or solo / Other
|
|
111483
|
+
|
|
111484
|
+
**Q3 \u2014 Company domain and size** (two quick selects, ask together)
|
|
111485
|
+
|
|
111486
|
+
- Domain: Technology / Finance & legal / Healthcare / Education / Retail & commerce / Industrial & manufacturing / Public sector / Services & consulting / Other
|
|
111487
|
+
- Size: Just me / Small team (2\u201320) / Mid-size company (20\u2013500) / Large organization (500+)
|
|
111488
|
+
|
|
111489
|
+
**Q4 \u2014 Bonus question** (free text, optional feel)
|
|
111490
|
+
|
|
111491
|
+
> "Is there any part of your job you'd love to hand off to someone else \u2014 even partially?"
|
|
111492
|
+
|
|
111493
|
+
This last question is the golden one: it surfaces self-identified pain in their own words, which feeds directly into Phase 2.
|
|
111494
|
+
|
|
111495
|
+
### Never skip Q4
|
|
111496
|
+
|
|
111497
|
+
Even if you feel you have enough context after Q3, always ask Q4. The bonus question often reveals the most actionable pain point and gives you their exact vocabulary to use in the skill.
|
|
111498
|
+
|
|
111499
|
+
---
|
|
111500
|
+
|
|
111501
|
+
## Phase 2: Suggest 3 Pain Points
|
|
111502
|
+
|
|
111503
|
+
Based on what you learned, **reason carefully** about what genuinely frustrates people in this role. Then propose exactly **3 pain points**, each one:
|
|
111504
|
+
|
|
111505
|
+
- A real, recurring frustration (not a vague category)
|
|
111506
|
+
- Solvable by Claude working with text, documents, or structured thinking
|
|
111507
|
+
- Requiring **no system integrations** (or at most: "paste text in" / "upload a file")
|
|
111508
|
+
- Described in plain human language
|
|
111509
|
+
|
|
111510
|
+
### Format for each pain point card
|
|
111511
|
+
|
|
111512
|
+
Present them conversationally, like this:
|
|
111513
|
+
|
|
111514
|
+
---
|
|
111515
|
+
|
|
111516
|
+
**1. [Short, vivid title]** *What happens:* [1 sentence describing the frustration in their words/world] *What a skill could do:* [1 concrete sentence \u2014 what Claude would actually produce] *Effort to set up:* Zero \u2014 just paste or upload
|
|
111517
|
+
|
|
111518
|
+
---
|
|
111519
|
+
|
|
111520
|
+
After presenting all 3, ask:
|
|
111521
|
+
|
|
111522
|
+
> "Do any of these ring a bell? Pick the one that would save you the most time or headaches."
|
|
111523
|
+
|
|
111524
|
+
Use \`ask_user_input_v0\` with the 3 titles as options (plus "None of these feel right").
|
|
111525
|
+
|
|
111526
|
+
If they say "none of these", ask one follow-up: "What's the thing at work that you keep putting off because it feels tedious?" Then generate 3 new suggestions.
|
|
111527
|
+
|
|
111528
|
+
---
|
|
111529
|
+
|
|
111530
|
+
## Phase 3: Interview Before Writing
|
|
111531
|
+
|
|
111532
|
+
Once they've picked a pain point, **don't jump straight to writing the skill**. Ask 2\u20133 short, focused questions to understand:
|
|
111533
|
+
|
|
111534
|
+
1. **A concrete example** \u2014 "Can you describe a real situation where this came up recently? Even roughly."
|
|
111535
|
+
2. **What good looks like** \u2014 "When it goes well, what does the result look like? (An email? A summary? A checklist?)"
|
|
111536
|
+
3. **The raw material** \u2014 "What do you usually start with? (A document, notes, an email thread, something else?)"
|
|
111537
|
+
|
|
111538
|
+
Keep these conversational. Use \`ask_user_input_v0\` where options make sense (e.g. output format), free text where you need specifics.
|
|
111539
|
+
|
|
111540
|
+
---
|
|
111541
|
+
|
|
111542
|
+
## Phase 4: Write the Skill Together
|
|
111543
|
+
|
|
111544
|
+
Now write a SKILL.md tailored to their pain point. Follow these principles:
|
|
111545
|
+
|
|
111546
|
+
### Skill writing rules for non-technical users
|
|
111547
|
+
|
|
111548
|
+
- **Name**: Short, verb-first, plain English (e.g. \`contract-plain-language\`, \`meeting-notes-to-actions\`)
|
|
111549
|
+
- **Description**: Written so skilder knows exactly when to offer this skill \u2014 include the phrases this person would naturally say
|
|
111550
|
+
- **Instructions**: Written as if explaining to a smart colleague, not a machine. No jargon.
|
|
111551
|
+
- **Input**: Always "paste your text below" or "upload your document" \u2014 never assume integrations
|
|
111552
|
+
- **Output**: One clear, usable thing (a draft, a list, a summary, a set of questions \u2014 not "analysis")
|
|
111553
|
+
|
|
111554
|
+
### Skill structure to follow
|
|
111555
|
+
|
|
111556
|
+
\`\`\`plaintext
|
|
111557
|
+
---
|
|
111558
|
+
name: [verb-noun]
|
|
111559
|
+
description: >
|
|
111560
|
+
Use this skill when [person's role] needs to [specific task].
|
|
111561
|
+
Trigger when they say things like "[natural phrase 1]", "[natural phrase 2]".
|
|
111562
|
+
---
|
|
111563
|
+
|
|
111564
|
+
# [Skill Name]
|
|
111565
|
+
|
|
111566
|
+
## What this does
|
|
111567
|
+
[1\u20132 sentences. Plain language.]
|
|
111568
|
+
|
|
111569
|
+
## How to use it
|
|
111570
|
+
[2\u20133 steps. Ultra simple. "Paste your text." "Tell skilder what you need." "Get your result."]
|
|
111571
|
+
|
|
111572
|
+
## What skilder will produce
|
|
111573
|
+
[Describe the output concretely.]
|
|
111574
|
+
|
|
111575
|
+
## Example
|
|
111576
|
+
Input: [short realistic example]
|
|
111577
|
+
Output: [what skilder would return]
|
|
111578
|
+
\`\`\`
|
|
111579
|
+
|
|
111580
|
+
### After writing
|
|
111581
|
+
|
|
111582
|
+
Show them the full skill file and ask:
|
|
111583
|
+
|
|
111584
|
+
> "Does this feel right? Is there anything about your situation I've got wrong or missed?"
|
|
111585
|
+
|
|
111586
|
+
Incorporate their feedback, then present the final \`.skill\` file for adding to workspace. If they ask about integrations (ex. spreadsheet folder, email, other systems or database), verify the tool list to see what's configured and accessible. If nothing exist or match, you must suggest to save the skill as-is and add Tools later. NEVER encourage manual work.
|
|
111587
|
+
|
|
111588
|
+
---
|
|
111589
|
+
|
|
111590
|
+
## Tone reminders (apply throughout)
|
|
111591
|
+
|
|
111592
|
+
- Never say "workflow", "automate", "prompt", "LLM", "model", or "integrate"
|
|
111593
|
+
- Say "save you time", "take care of the tedious part", "handle the first draft"
|
|
111594
|
+
- If they seem unsure or intimidated, reassure: "You don't need to know anything technical \u2014 we're just figuring out what would be most useful for you."
|
|
111595
|
+
- Keep every message short. One idea at a time.`,
|
|
111596
|
+
related: []
|
|
111597
|
+
}
|
|
111598
|
+
];
|
|
111599
|
+
|
|
111600
|
+
// ../common/src/skill-catalog-business.ts
|
|
111601
|
+
var BUSINESS_FEATURED_SKILLS = [
|
|
111602
|
+
{
|
|
111603
|
+
slug: "product-and-service-catalog",
|
|
111604
|
+
name: "Product & Service Catalog",
|
|
111605
|
+
description: `Use this skill to answer employee questions about what the company sells: products, services, descriptions, pricing, packages, and who owns each line. Trigger on questions like "what's the price of X", "do we offer Y", "describe service Z", "what's included in our Premium plan", "who do I talk to for X".`,
|
|
111606
|
+
tags: ["business", "reference", "template"],
|
|
111607
|
+
instructions: `Answer employee questions about the company's products and services using the catalog reference document.
|
|
111608
|
+
|
|
111609
|
+
## How to use
|
|
111610
|
+
|
|
111611
|
+
1. Read @references/product-catalog.md to find the matching product or service entry.
|
|
111612
|
+
2. Quote the relevant fields directly (name, description, price, status, contact).
|
|
111613
|
+
3. If the question is comparative ("which plan fits a small team?"), summarise the relevant rows in plain language \u2014 don't dump the full table.
|
|
111614
|
+
4. If the data hasn't been filled in yet (placeholders like {Product Name} still present), tell the user the catalog hasn't been populated and point them at the template.
|
|
111615
|
+
|
|
111616
|
+
## Constraints
|
|
111617
|
+
|
|
111618
|
+
- **Do not invent products, prices, or features.** If it isn't in the catalog, say so and offer to flag it for the catalog owner.
|
|
111619
|
+
- **Mention status fields** when present (e.g. "Discontinued", "Beta", "Coming soon"). Don't quote prices for inactive items without flagging the status.
|
|
111620
|
+
- **Internal pricing or margin info** stays internal \u2014 if the reference marks a row as "Confidential", do not share with external-facing roles without confirming the requester's permission.
|
|
111621
|
+
|
|
111622
|
+
## Maintenance
|
|
111623
|
+
|
|
111624
|
+
The catalog template lives in @references/product-catalog.md. Replace placeholder rows with your real product lines; the structure is intentionally flat to make updates fast. Owners are expected to keep status and pricing accurate.`,
|
|
111625
|
+
related: [
|
|
111626
|
+
{
|
|
111627
|
+
name: "product-catalog.md",
|
|
111628
|
+
description: "Template product & service catalog \u2014 replace placeholder rows with real data.",
|
|
111629
|
+
mimetype: "text/markdown",
|
|
111630
|
+
content: `# Product & Service Catalog
|
|
111631
|
+
|
|
111632
|
+
> **Template \u2014 replace the example rows with your real products and services.** Keep the column structure so the agent can read the data reliably.
|
|
111633
|
+
|
|
111634
|
+
## Active products & services
|
|
111635
|
+
|
|
111636
|
+
| Name | Category | Description | Price | Unit | Status | Owner |
|
|
111637
|
+
|---|---|---|---|---|---|---|
|
|
111638
|
+
| {Product A} | {Hardware / Software / Service} | {One-line description of what it does} | {0.00} | {per unit / per month / per project} | Active | {Owner name or team} |
|
|
111639
|
+
| {Product B} | {Category} | {Description} | {0.00} | {Unit} | Active | {Owner} |
|
|
111640
|
+
| {Service C} | Service | {Description} | {0.00} | per hour | Active | {Owner} |
|
|
111641
|
+
|
|
111642
|
+
## Packages & bundles
|
|
111643
|
+
|
|
111644
|
+
| Bundle | Includes | Price | Notes |
|
|
111645
|
+
|---|---|---|---|
|
|
111646
|
+
| {Starter Pack} | {Product A + Product B} | {0.00} | {Volume discount, contract length, etc.} |
|
|
111647
|
+
|
|
111648
|
+
## Inactive / archived
|
|
111649
|
+
|
|
111650
|
+
| Name | Status | Replaced by |
|
|
111651
|
+
|---|---|---|
|
|
111652
|
+
| {Old Product} | Discontinued ({date}) | {New Product} |
|
|
111653
|
+
|
|
111654
|
+
## Maintenance notes
|
|
111655
|
+
|
|
111656
|
+
- Update the **Status** column whenever a product is launched, paused, or retired.
|
|
111657
|
+
- Mark rows **Confidential** in the Notes column if internal-only pricing should not be shared externally.
|
|
111658
|
+
- Keep one row per SKU \u2014 split bundles into the Packages section.
|
|
111659
|
+
`
|
|
111660
|
+
}
|
|
111661
|
+
]
|
|
111662
|
+
},
|
|
111663
|
+
{
|
|
111664
|
+
slug: "customer-directory",
|
|
111665
|
+
name: "Customer Directory",
|
|
111666
|
+
description: `Use this skill to look up information about the company's customers: contact details, account manager, account status, recent interactions. Trigger on questions like "who is X", "what's the account manager for Y", "how do we contact Z", "what's the status of customer X", "when did we last talk to Y".`,
|
|
111667
|
+
tags: ["business", "reference", "template"],
|
|
111668
|
+
instructions: `Help employees find customer information using the customer directory reference document.
|
|
111669
|
+
|
|
111670
|
+
## How to use
|
|
111671
|
+
|
|
111672
|
+
1. Read @references/customer-directory.md to locate the customer by name, company, or account ID.
|
|
111673
|
+
2. Share the requested field(s): contact, account manager, status, last interaction date.
|
|
111674
|
+
3. For broader questions ("which customers are inactive?"), summarise the matching rows.
|
|
111675
|
+
4. If the data still contains placeholders, tell the user the directory hasn't been populated yet.
|
|
111676
|
+
|
|
111677
|
+
## Privacy & access
|
|
111678
|
+
|
|
111679
|
+
- @references/customer-access-policy.md defines what to share with whom. Follow it.
|
|
111680
|
+
- Default to sharing only **business contact info** (name, role, work email/phone, account status).
|
|
111681
|
+
- **Do not share** financial details, contract terms, or personal info to roles not listed as "Full access" in the access policy.
|
|
111682
|
+
- If unsure who has access, ask the requester to confirm their role before answering.
|
|
111683
|
+
|
|
111684
|
+
## Constraints
|
|
111685
|
+
|
|
111686
|
+
- Don't invent or guess contact details. If a customer isn't listed, say so.
|
|
111687
|
+
- For sensitive lookups (account churn, contract value), confirm the requester's role first.
|
|
111688
|
+
|
|
111689
|
+
## Maintenance
|
|
111690
|
+
|
|
111691
|
+
Keep @references/customer-directory.md up to date when account managers change or status flips. The access policy is the source of truth for who can see what.`,
|
|
111692
|
+
related: [
|
|
111693
|
+
{
|
|
111694
|
+
name: "customer-directory.md",
|
|
111695
|
+
description: "Template customer directory \u2014 replace placeholder rows with real customers.",
|
|
111696
|
+
mimetype: "text/markdown",
|
|
111697
|
+
content: `# Customer Directory
|
|
111698
|
+
|
|
111699
|
+
> **Template \u2014 replace the example rows with your real customer list.** Keep the columns so the agent can answer lookups reliably.
|
|
111700
|
+
|
|
111701
|
+
| Account ID | Company | Primary Contact | Role | Email | Phone | Account Manager | Status | Last Interaction | Notes |
|
|
111702
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
111703
|
+
| {ACME-001} | {Acme Corp} | {Jane Doe} | {Head of Ops} | {jane@acme.example} | {+1 555 0100} | {Account Manager Name} | Active | {YYYY-MM-DD} | {Renewal Q3} |
|
|
111704
|
+
| {GLOBEX-002} | {Globex} | {John Smith} | {VP Sales} | {john@globex.example} | {+1 555 0101} | {Account Manager Name} | At-risk | {YYYY-MM-DD} | {Pricing escalation open} |
|
|
111705
|
+
| {INITECH-003} | {Initech} | {Alex Wong} | {Procurement Lead} | {alex@initech.example} | {+1 555 0102} | {Account Manager Name} | Inactive | {YYYY-MM-DD} | {Contract ended {date}} |
|
|
111706
|
+
|
|
111707
|
+
## Status legend
|
|
111708
|
+
|
|
111709
|
+
- **Active** \u2014 current paying customer
|
|
111710
|
+
- **At-risk** \u2014 flagged by account manager; escalation in flight
|
|
111711
|
+
- **Inactive** \u2014 no current contract; do not contact without approval
|
|
111712
|
+
- **Prospect** \u2014 in sales pipeline, not yet a customer
|
|
111713
|
+
|
|
111714
|
+
## Maintenance notes
|
|
111715
|
+
|
|
111716
|
+
- Update **Status** and **Last Interaction** after each meaningful touchpoint.
|
|
111717
|
+
- Mark Notes as **Confidential** if they reference negotiation or churn risk.
|
|
111718
|
+
`
|
|
111719
|
+
},
|
|
111720
|
+
{
|
|
111721
|
+
name: "customer-access-policy.md",
|
|
111722
|
+
description: "Template access policy \u2014 who can see which customer fields.",
|
|
111723
|
+
mimetype: "text/markdown",
|
|
111724
|
+
content: `# Customer Information Access Policy
|
|
111725
|
+
|
|
111726
|
+
> **Template \u2014 replace placeholder roles with the actual roles in your workspace.** This policy controls what the Business Assistant will share with each requester.
|
|
111727
|
+
|
|
111728
|
+
## Access levels
|
|
111729
|
+
|
|
111730
|
+
| Role | Contact info | Account status | Account manager | Financial / contract | Sensitive notes |
|
|
111731
|
+
|---|---|---|---|---|---|
|
|
111732
|
+
| {Sales team} | \u2705 | \u2705 | \u2705 | \u2705 | \u2705 |
|
|
111733
|
+
| {Customer support} | \u2705 | \u2705 | \u2705 | \u274C | \u274C |
|
|
111734
|
+
| {Operations / delivery} | \u2705 | \u2705 | \u2705 | \u274C | \u274C |
|
|
111735
|
+
| {General employee} | \u2705 business email only | \u2705 | \u2705 | \u274C | \u274C |
|
|
111736
|
+
| {External / unknown} | \u274C | \u274C | \u274C | \u274C | \u274C |
|
|
111737
|
+
|
|
111738
|
+
## When to refuse
|
|
111739
|
+
|
|
111740
|
+
- The requester's role isn't listed \u2192 ask them to confirm their role first.
|
|
111741
|
+
- The customer is marked **Inactive** \u2192 require explicit approval from the listed account manager before sharing.
|
|
111742
|
+
- Notes are flagged **Confidential** \u2192 do not share unless the role has "Sensitive notes" access.
|
|
111743
|
+
|
|
111744
|
+
## When to escalate
|
|
111745
|
+
|
|
111746
|
+
- Requests for bulk export of the directory \u2192 escalate to the data owner.
|
|
111747
|
+
- Repeated lookups across many customers from a single user \u2192 flag to the workspace admin.
|
|
111748
|
+
`
|
|
111749
|
+
}
|
|
111750
|
+
]
|
|
111751
|
+
},
|
|
111752
|
+
{
|
|
111753
|
+
slug: "supplier-directory",
|
|
111754
|
+
name: "Supplier Directory",
|
|
111755
|
+
description: `Use this skill to look up information about the company's suppliers and vendors: who supplies what, contact details, contract end dates, lead times, payment terms. Trigger on questions like "who supplies X", "how do we contact supplier Y", "when does our contract with Z expire", "what's our lead time for component A".`,
|
|
111756
|
+
tags: ["business", "reference", "template"],
|
|
111757
|
+
instructions: `Answer employee questions about suppliers and vendors using the supplier directory reference document.
|
|
111758
|
+
|
|
111759
|
+
## How to use
|
|
111760
|
+
|
|
111761
|
+
1. Read @references/supplier-directory.md to find the supplier by name, category, or what they provide.
|
|
111762
|
+
2. Share the requested fields: contact, contract end date, lead time, payment terms.
|
|
111763
|
+
3. For "who supplies X" questions, scan the Category and What we buy columns; list all matches.
|
|
111764
|
+
4. If the data still contains placeholders, tell the user the directory hasn't been populated yet.
|
|
111765
|
+
|
|
111766
|
+
## Constraints
|
|
111767
|
+
|
|
111768
|
+
- **Don't invent suppliers or contract terms.** If something isn't listed, say so.
|
|
111769
|
+
- **Flag expiring contracts** proactively when the user asks about a supplier whose contract end date is within 60 days.
|
|
111770
|
+
- **Confidential pricing** (marked in the Notes column) should not be shared outside procurement without confirmation.
|
|
111771
|
+
|
|
111772
|
+
## Maintenance
|
|
111773
|
+
|
|
111774
|
+
The directory at @references/supplier-directory.md is the source of truth. Procurement owners are expected to keep contract dates and contacts current; flag stale entries (no interaction in 12+ months) for review.`,
|
|
111775
|
+
related: [
|
|
111776
|
+
{
|
|
111777
|
+
name: "supplier-directory.md",
|
|
111778
|
+
description: "Template supplier directory \u2014 replace placeholder rows with real suppliers.",
|
|
111779
|
+
mimetype: "text/markdown",
|
|
111780
|
+
content: `# Supplier Directory
|
|
111781
|
+
|
|
111782
|
+
> **Template \u2014 replace the example rows with your real suppliers.** Keep the columns so the agent can answer lookups reliably.
|
|
111783
|
+
|
|
111784
|
+
| Supplier | Category | What we buy | Primary Contact | Email | Phone | Contract End | Lead Time | Payment Terms | Notes |
|
|
111785
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
111786
|
+
| {Supplier A} | {Hardware} | {Specific components} | {Contact name} | {contact@supplierA.example} | {+1 555 0200} | {YYYY-MM-DD} | {2 weeks} | {Net 30} | {Preferred vendor} |
|
|
111787
|
+
| {Supplier B} | {Services} | {Cleaning, facilities} | {Contact name} | {contact@supplierB.example} | {+1 555 0201} | {YYYY-MM-DD} | {N/A} | {Net 15} | {Auto-renews annually} |
|
|
111788
|
+
| {Supplier C} | {Software / SaaS} | {Specific tool} | {Contact name} | {contact@supplierC.example} | {+1 555 0202} | {YYYY-MM-DD} | {Immediate} | {Annual prepay} | {Confidential pricing} |
|
|
111789
|
+
|
|
111790
|
+
## Category legend
|
|
111791
|
+
|
|
111792
|
+
Common categories: Hardware, Software / SaaS, Services, Logistics, Marketing, Professional services, Utilities.
|
|
111793
|
+
|
|
111794
|
+
## Maintenance notes
|
|
111795
|
+
|
|
111796
|
+
- Update **Contract End** whenever a renewal or termination happens.
|
|
111797
|
+
- Mark Notes as **Confidential** for negotiated pricing or sensitive terms.
|
|
111798
|
+
- Archive (don't delete) ended suppliers \u2014 keep a row with Status: Archived for institutional memory.
|
|
111799
|
+
`
|
|
111800
|
+
}
|
|
111801
|
+
]
|
|
111802
|
+
},
|
|
111803
|
+
{
|
|
111804
|
+
slug: "team-weekly-planning",
|
|
111805
|
+
name: "Team Weekly Planning",
|
|
111806
|
+
description: `Use this skill to answer questions about who's working on what this week, who's on leave, project assignments, and team availability. Trigger on questions like "who's working on X this week", "is Y available", "who's on PTO this week", "what's the team doing", "who can I ask about project Z".`,
|
|
111807
|
+
tags: ["business", "reference", "template"],
|
|
111808
|
+
instructions: `Answer employee questions about team availability and assignments using the weekly planning reference document.
|
|
111809
|
+
|
|
111810
|
+
## How to use
|
|
111811
|
+
|
|
111812
|
+
1. Read @references/weekly-planning.md for the current week's assignments and availability.
|
|
111813
|
+
2. For "who's working on X", list the assigned team members and their availability for the week.
|
|
111814
|
+
3. For "is Y available", report the requested person's PTO and assignment load for the week.
|
|
111815
|
+
4. For "who's on PTO", list everyone with a PTO entry for the requested week.
|
|
111816
|
+
5. If the planning is stale (week marker is in the past), call this out and recommend asking the team lead for the current week.
|
|
111817
|
+
|
|
111818
|
+
## Constraints
|
|
111819
|
+
|
|
111820
|
+
- **Don't invent assignments or PTO.** If someone isn't listed, say so and suggest asking their manager.
|
|
111821
|
+
- **The planning reflects intent, not real-time status.** If the user needs live availability (e.g. "can Y join a call now?"), tell them to message the person directly.
|
|
111822
|
+
- **PTO reasons stay private.** Share "on PTO" but not the reason unless explicitly listed as public.
|
|
111823
|
+
|
|
111824
|
+
## Maintenance
|
|
111825
|
+
|
|
111826
|
+
The planning at @references/weekly-planning.md should be refreshed weekly (typically at week start by the team lead). Stale planning is worse than no planning \u2014 flag week dates older than 7 days for refresh.`,
|
|
111827
|
+
related: [
|
|
111828
|
+
{
|
|
111829
|
+
name: "weekly-planning.md",
|
|
111830
|
+
description: "Template weekly planning \u2014 replace placeholder rows with real assignments.",
|
|
111831
|
+
mimetype: "text/markdown",
|
|
111832
|
+
content: `# Team Weekly Planning
|
|
111833
|
+
|
|
111834
|
+
> **Template \u2014 replace placeholders with this week's real assignments.** Update at the start of each week (typically Monday).
|
|
111835
|
+
|
|
111836
|
+
## Week of {YYYY-MM-DD}
|
|
111837
|
+
|
|
111838
|
+
### Assignments
|
|
111839
|
+
|
|
111840
|
+
| Person | Role | Primary project | Other work | Availability | Notes |
|
|
111841
|
+
|---|---|---|---|---|---|
|
|
111842
|
+
| {Alice} | {Engineer} | {Project Alpha} | {Code reviews, on-call Tue} | Full week | {Focus blocks 9\u201312 daily} |
|
|
111843
|
+
| {Bob} | {Designer} | {Project Beta} | {Design system} | Mon\u2013Wed | {PTO Thu\u2013Fri} |
|
|
111844
|
+
| {Carol} | {PM} | {Project Alpha, Project Beta} | {Roadmap planning} | Full week | {} |
|
|
111845
|
+
| {Dan} | {Engineer} | \u2014 | \u2014 | PTO full week | {Back {YYYY-MM-DD}} |
|
|
111846
|
+
|
|
111847
|
+
### Out of office this week
|
|
111848
|
+
|
|
111849
|
+
| Person | Dates | Cover |
|
|
111850
|
+
|---|---|---|
|
|
111851
|
+
| {Dan} | {Mon\u2013Fri} | {Alice covers on-call} |
|
|
111852
|
+
| {Bob} | {Thu\u2013Fri} | {N/A \u2014 async} |
|
|
111853
|
+
|
|
111854
|
+
### Project leads (current quarter)
|
|
111855
|
+
|
|
111856
|
+
| Project | Lead | Backup |
|
|
111857
|
+
|---|---|---|
|
|
111858
|
+
| {Project Alpha} | {Carol} | {Alice} |
|
|
111859
|
+
| {Project Beta} | {Carol} | {Bob} |
|
|
111860
|
+
|
|
111861
|
+
## Maintenance notes
|
|
111862
|
+
|
|
111863
|
+
- Update **Week of** date every Monday. Stale dates make the agent give wrong answers.
|
|
111864
|
+
- Use **PTO**, **Half-day**, **Remote-only** in the Availability column for quick scanning.
|
|
111865
|
+
- Keep Project leads stable across the quarter \u2014 they're how people know who to escalate to.
|
|
111866
|
+
`
|
|
111867
|
+
}
|
|
111868
|
+
]
|
|
111869
|
+
}
|
|
111870
|
+
];
|
|
111871
|
+
|
|
111872
|
+
// ../common/src/skill-catalog.ts
|
|
111873
|
+
var FEATURED_SKILLS = [
|
|
111874
|
+
...SKILDER_META_FEATURED_SKILLS,
|
|
111875
|
+
...ONBOARDING_FEATURED_SKILLS,
|
|
111876
|
+
...BUSINESS_FEATURED_SKILLS
|
|
111877
|
+
];
|
|
111878
|
+
|
|
111879
|
+
// ../common/src/hat-catalog.ts
|
|
111880
|
+
var FEATURED_HATS = [
|
|
111881
|
+
{
|
|
111882
|
+
slug: "business-assistant",
|
|
111883
|
+
name: "Business Assistant",
|
|
111884
|
+
description: "Central internal info desk: any employee can ask about products, customers, suppliers, or this week's team plan and get a fast, sourced answer.",
|
|
111885
|
+
color: "BLUE",
|
|
111886
|
+
tags: ["business", "reference", "template"],
|
|
111887
|
+
skillSlugs: [
|
|
111888
|
+
"product-and-service-catalog",
|
|
111889
|
+
"customer-directory",
|
|
111890
|
+
"supplier-directory",
|
|
111891
|
+
"team-weekly-planning"
|
|
111892
|
+
],
|
|
111893
|
+
instructions: `You are the company's internal Business Assistant. Any employee can ask you about what we sell, who our customers and suppliers are, and what the team is working on this week. Your job is to give a fast, sourced answer \u2014 or to clearly say when you don't have the data.
|
|
111894
|
+
|
|
111895
|
+
## Which skill to use
|
|
111896
|
+
|
|
111897
|
+
- **What we sell** (products, services, pricing, packages) \u2192 Product & Service Catalog
|
|
111898
|
+
- **A specific customer** (contact, account manager, status) \u2192 Customer Directory
|
|
111899
|
+
- **A supplier or vendor** (contact, contract, lead time) \u2192 Supplier Directory
|
|
111900
|
+
- **Who's working on what / who's available** \u2192 Team Weekly Planning
|
|
111901
|
+
|
|
111902
|
+
If a question spans skills (e.g. "which account manager handles Acme, and is she on PTO?"), use both skills in turn and combine the answer.
|
|
111903
|
+
|
|
111904
|
+
## Operating principles
|
|
111905
|
+
|
|
111906
|
+
- **Source every answer.** Quote the relevant rows from the reference document, don't paraphrase from memory.
|
|
111907
|
+
- **Don't invent data.** If the requested item isn't in the reference, say so plainly and suggest who owns the catalog so the user can ask for it to be added.
|
|
111908
|
+
- **Respect access policies.** The Customer Directory skill carries an access policy \u2014 follow it. If unsure who can see what, ask before sharing.
|
|
111909
|
+
- **Flag stale templates.** If a reference still contains placeholder values (\`{Product Name}\`, \`{Acme Corp}\`), tell the user the workspace hasn't populated it yet and point them at the template.
|
|
111910
|
+
- **Be concise.** Employees want a quick answer, not a wall of text. One or two lines for simple lookups; a short list for "who" or "which" questions.
|
|
111911
|
+
|
|
111912
|
+
## Template hat \u2014 first-time setup
|
|
111913
|
+
|
|
111914
|
+
This hat ships as a template. The bundled skills each include a reference document with placeholder content. Before this assistant is useful in your workspace, an owner needs to fill in the actual product list, customer directory, supplier directory, and weekly planning. The templates are intentionally simple markdown tables \u2014 open each skill's references tab and replace placeholders with your real data.`
|
|
111915
|
+
},
|
|
111916
|
+
{
|
|
111917
|
+
slug: SKILDER_HAT_DEFAULTS.slug,
|
|
111918
|
+
name: SKILDER_HAT_DEFAULTS.name,
|
|
111919
|
+
description: SKILDER_HAT_DEFAULTS.description,
|
|
111920
|
+
color: SKILDER_HAT_DEFAULTS.color,
|
|
111921
|
+
tags: ["skilder", "meta", "admin"],
|
|
111922
|
+
skillSlugs: [...SKILDER_SKILL_IDS],
|
|
111923
|
+
instructions: SKILDER_HAT_DEFAULTS.instructions
|
|
111924
|
+
}
|
|
111925
|
+
];
|
|
111926
|
+
|
|
110768
111927
|
// ../common/src/services/logger.service.ts
|
|
110769
111928
|
var import_pino = __toESM(require_pino(), 1);
|
|
110770
111929
|
var import_pino_pretty = __toESM(require_pino_pretty(), 1);
|
|
@@ -111426,6 +112585,51 @@ var EncryptionService = class EncryptionService2 {
|
|
|
111426
112585
|
isEncrypted(value) {
|
|
111427
112586
|
return value.startsWith(ENCRYPTED_PREFIX);
|
|
111428
112587
|
}
|
|
112588
|
+
/**
|
|
112589
|
+
* Compute a SHA-256 hash (hex) of an opaque value.
|
|
112590
|
+
*
|
|
112591
|
+
* Used for one-way hashing of high-entropy secrets (OAuth access/refresh tokens,
|
|
112592
|
+
* authorization codes, client secrets, registration access tokens) so that only
|
|
112593
|
+
* the hash is stored at rest and lookups happen by hash. Plain SHA-256 is
|
|
112594
|
+
* appropriate here because the inputs are 256-bit random opaque values with no
|
|
112595
|
+
* brute-force surface; this is NOT for hashing low-entropy user passwords.
|
|
112596
|
+
*/
|
|
112597
|
+
sha256Hash(value) {
|
|
112598
|
+
return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
|
|
112599
|
+
}
|
|
112600
|
+
/**
|
|
112601
|
+
* Constant-time equality of two strings compared as UTF-8 bytes. Returns false
|
|
112602
|
+
* on any length mismatch (and for empty input). Use for comparing fixed-length,
|
|
112603
|
+
* high-entropy values that are already in their final comparable form — e.g. a
|
|
112604
|
+
* recomputed PKCE S256 challenge against the stored one — without leaking
|
|
112605
|
+
* content via timing. For comparing a raw secret against a stored hash, use
|
|
112606
|
+
* {@link verifySha256Hash} instead.
|
|
112607
|
+
*/
|
|
112608
|
+
constantTimeEquals(a3, b2) {
|
|
112609
|
+
const ab = Buffer.from(a3, "utf8");
|
|
112610
|
+
const bb = Buffer.from(b2, "utf8");
|
|
112611
|
+
if (ab.length !== bb.length || ab.length === 0) {
|
|
112612
|
+
return false;
|
|
112613
|
+
}
|
|
112614
|
+
return (0, import_crypto.timingSafeEqual)(ab, bb);
|
|
112615
|
+
}
|
|
112616
|
+
/**
|
|
112617
|
+
* Constant-time comparison of a raw value against a stored SHA-256 hex hash.
|
|
112618
|
+
* Avoids timing side channels when verifying secrets.
|
|
112619
|
+
*/
|
|
112620
|
+
verifySha256Hash(rawValue, storedHash) {
|
|
112621
|
+
const computed = Buffer.from(this.sha256Hash(rawValue), "hex");
|
|
112622
|
+
let expected;
|
|
112623
|
+
try {
|
|
112624
|
+
expected = Buffer.from(storedHash, "hex");
|
|
112625
|
+
} catch {
|
|
112626
|
+
return false;
|
|
112627
|
+
}
|
|
112628
|
+
if (computed.length !== expected.length || expected.length === 0) {
|
|
112629
|
+
return false;
|
|
112630
|
+
}
|
|
112631
|
+
return (0, import_crypto.timingSafeEqual)(computed, expected);
|
|
112632
|
+
}
|
|
111429
112633
|
};
|
|
111430
112634
|
EncryptionService = __decorate3([
|
|
111431
112635
|
W(),
|
|
@@ -139057,7 +140261,8 @@ function detectRelatedMentions(content, related) {
|
|
|
139057
140261
|
}
|
|
139058
140262
|
searchTerms.push(r2.name);
|
|
139059
140263
|
for (const term of searchTerms) {
|
|
139060
|
-
if (seen.has(qualifiedName))
|
|
140264
|
+
if (seen.has(qualifiedName))
|
|
140265
|
+
break;
|
|
139061
140266
|
try {
|
|
139062
140267
|
const pattern = buildSearchPattern(term);
|
|
139063
140268
|
if (pattern.test(content)) {
|
|
@@ -139069,17 +140274,12 @@ function detectRelatedMentions(content, related) {
|
|
|
139069
140274
|
});
|
|
139070
140275
|
}
|
|
139071
140276
|
} catch (error48) {
|
|
139072
|
-
console.warn(
|
|
139073
|
-
`[detectRelatedMentions] Failed to match term "${term}" for resource "${r2.name}":`,
|
|
139074
|
-
error48
|
|
139075
|
-
);
|
|
140277
|
+
console.warn(`[detectRelatedMentions] Failed to match term "${term}" for resource "${r2.name}":`, error48);
|
|
139076
140278
|
continue;
|
|
139077
140279
|
}
|
|
139078
140280
|
}
|
|
139079
140281
|
}
|
|
139080
|
-
return Array.from(seen.values()).sort(
|
|
139081
|
-
(a3, b2) => a3.qualifiedName.localeCompare(b2.qualifiedName)
|
|
139082
|
-
);
|
|
140282
|
+
return Array.from(seen.values()).sort((a3, b2) => a3.qualifiedName.localeCompare(b2.qualifiedName));
|
|
139083
140283
|
}
|
|
139084
140284
|
|
|
139085
140285
|
// src/utils.ts
|
|
@@ -144219,6 +145419,25 @@ var path2 = __toESM(require("path"));
|
|
|
144219
145419
|
var import_node_crypto3 = require("node:crypto");
|
|
144220
145420
|
var import_promises = require("node:stream/promises");
|
|
144221
145421
|
var import_node_stream = require("node:stream");
|
|
145422
|
+
|
|
145423
|
+
// src/config/urls.ts
|
|
145424
|
+
function publicMcpUrl() {
|
|
145425
|
+
return parseUrlEnv("PUBLIC_MCP_URL", "http://localhost:3001/mcp");
|
|
145426
|
+
}
|
|
145427
|
+
function publicAuthorizationServerUrl() {
|
|
145428
|
+
return parseUrlEnv("PUBLIC_BACKEND_URL", "http://localhost:3000");
|
|
145429
|
+
}
|
|
145430
|
+
function privateMcpUrl() {
|
|
145431
|
+
return parseUrlEnv("PRIVATE_MCP_URL", "http://localhost:3001/mcp");
|
|
145432
|
+
}
|
|
145433
|
+
function privateBackendUrl() {
|
|
145434
|
+
return parseUrlEnv("PRIVATE_BACKEND_URL", "http://localhost:3000");
|
|
145435
|
+
}
|
|
145436
|
+
function natsServers() {
|
|
145437
|
+
return parseUrlEnv("NATS_SERVERS", "localhost:4222");
|
|
145438
|
+
}
|
|
145439
|
+
|
|
145440
|
+
// src/services/file.service.ts
|
|
144222
145441
|
var __decorate8 = function(decorators, target, key, desc) {
|
|
144223
145442
|
var c3 = arguments.length, r2 = c3 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d2;
|
|
144224
145443
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r2 = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -144294,8 +145513,9 @@ var FileService = class FileService2 extends LifecycleService {
|
|
|
144294
145513
|
SKILDER_ALLOWED_PATHS: this.allowedPaths.join(":"),
|
|
144295
145514
|
SKILDER_MAX_FILE_SIZE_MB: String(this.maxFileSizeMB)
|
|
144296
145515
|
};
|
|
144297
|
-
|
|
144298
|
-
|
|
145516
|
+
const backendUrl = process.env.SKILDER_BACKEND_URL || (process.env.PRIVATE_BACKEND_URL ? privateBackendUrl() : void 0);
|
|
145517
|
+
if (backendUrl) {
|
|
145518
|
+
env.SKILDER_BACKEND_URL = backendUrl;
|
|
144299
145519
|
}
|
|
144300
145520
|
return env;
|
|
144301
145521
|
}
|
|
@@ -144595,9 +145815,9 @@ var DelegateService = class DelegateService2 {
|
|
|
144595
145815
|
const modelString = modelOverride || defaultModel;
|
|
144596
145816
|
const { slug, modelName } = this.aiProviderService.parseModelString(modelString);
|
|
144597
145817
|
const model = this.aiProviderService.getProviderModel(provider, modelName, providerConfig, slug);
|
|
144598
|
-
const runtimeUrl =
|
|
144599
|
-
if (!process.env.
|
|
144600
|
-
this.logger.warn({ runtimeUrl, event: "delegate_mcp_url_fallback" }, "
|
|
145818
|
+
const runtimeUrl = privateMcpUrl();
|
|
145819
|
+
if (!process.env.PRIVATE_MCP_URL) {
|
|
145820
|
+
this.logger.warn({ runtimeUrl, event: "delegate_mcp_url_fallback" }, "PRIVATE_MCP_URL not set, falling back to default");
|
|
144601
145821
|
}
|
|
144602
145822
|
if (!userKey) {
|
|
144603
145823
|
return { isError: true, content: [{ type: "text", text: "Error: User key not available for delegate MCP connection." }] };
|
|
@@ -145187,7 +146407,7 @@ var _a35;
|
|
|
145187
146407
|
var CONTENT_CACHE_LRU_SIZE = 256;
|
|
145188
146408
|
var CONTENT_CACHE_TTL_MS = 3e5;
|
|
145189
146409
|
function getBackendUrl2() {
|
|
145190
|
-
return
|
|
146410
|
+
return privateBackendUrl();
|
|
145191
146411
|
}
|
|
145192
146412
|
var SkillContentService = class SkillContentService2 {
|
|
145193
146413
|
constructor(loggerService) {
|
|
@@ -145847,7 +147067,7 @@ ${JSON.stringify(toolConfig.inputSchema, null, 2)}`
|
|
|
145847
147067
|
// src/services/static-tools/download-asset.handler.ts
|
|
145848
147068
|
var import_node_stream2 = require("node:stream");
|
|
145849
147069
|
function getBackendUrl3() {
|
|
145850
|
-
return (
|
|
147070
|
+
return privateBackendUrl();
|
|
145851
147071
|
}
|
|
145852
147072
|
var DownloadAssetError = class extends Error {
|
|
145853
147073
|
constructor(userMessage, skillId) {
|
|
@@ -146090,7 +147310,7 @@ var MIME_BY_EXTENSION = {
|
|
|
146090
147310
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
146091
147311
|
};
|
|
146092
147312
|
function getBackendUrl4() {
|
|
146093
|
-
return (
|
|
147313
|
+
return privateBackendUrl();
|
|
146094
147314
|
}
|
|
146095
147315
|
function mimeFromExtension(filePath) {
|
|
146096
147316
|
const ext = path4.extname(filePath).toLowerCase();
|
|
@@ -147289,7 +148509,11 @@ var FastifyManagerService = class FastifyManagerService2 extends LifecycleServic
|
|
|
147289
148509
|
// Allow credentials (cookies, authorization headers, etc.)
|
|
147290
148510
|
methods: ["GET", "POST", "OPTIONS", "PUT", "DELETE"],
|
|
147291
148511
|
// Explicitly allow methods including GET for SSE
|
|
147292
|
-
|
|
148512
|
+
// WWW-Authenticate must be exposed so browser-based MCP clients (e.g. the
|
|
148513
|
+
// MCP Inspector in direct mode) can read the RFC 9728 `resource_metadata`
|
|
148514
|
+
// challenge on a 401 and discover the Authorization Server. Without it the
|
|
148515
|
+
// browser hides the header and the client falls back to the wrong origin.
|
|
148516
|
+
exposedHeaders: ["Mcp-Session-Id", "WWW-Authenticate"],
|
|
147293
148517
|
allowedHeaders: [
|
|
147294
148518
|
"Content-Type",
|
|
147295
148519
|
"mcp-session-id",
|
|
@@ -148376,7 +149600,7 @@ function injectCorsHeaders(request, reply) {
|
|
|
148376
149600
|
}
|
|
148377
149601
|
finalHeaders["Access-Control-Allow-Origin"] = origin;
|
|
148378
149602
|
finalHeaders["Access-Control-Allow-Credentials"] = "true";
|
|
148379
|
-
finalHeaders["Access-Control-Expose-Headers"] = "Mcp-Session-Id";
|
|
149603
|
+
finalHeaders["Access-Control-Expose-Headers"] = "Mcp-Session-Id, WWW-Authenticate";
|
|
148380
149604
|
if (finalStatusMessage) {
|
|
148381
149605
|
return originalWriteHead(statusCode, finalStatusMessage, finalHeaders);
|
|
148382
149606
|
} else {
|
|
@@ -148507,12 +149731,21 @@ function getStringHeader(headers, name21) {
|
|
|
148507
149731
|
const value = headers[name21];
|
|
148508
149732
|
return typeof value === "string" && value ? value : void 0;
|
|
148509
149733
|
}
|
|
149734
|
+
function getBearerToken(headers) {
|
|
149735
|
+
const value = headers["authorization"];
|
|
149736
|
+
if (typeof value !== "string") {
|
|
149737
|
+
return void 0;
|
|
149738
|
+
}
|
|
149739
|
+
const match = /^Bearer\s+(.+)$/i.exec(value.trim());
|
|
149740
|
+
const token = match?.[1].trim();
|
|
149741
|
+
return token ? token : void 0;
|
|
149742
|
+
}
|
|
148510
149743
|
function extractAuthHeaders(request) {
|
|
148511
149744
|
const headers = request.headers;
|
|
148512
149745
|
const query = request.query;
|
|
148513
149746
|
const skillIds = query?.skillIds?.split(",").filter(Boolean);
|
|
148514
149747
|
const hatIds = query?.hatIds?.split(",").filter(Boolean);
|
|
148515
|
-
const userKey = query?.key || getStringHeader(headers, "user_key") || getStringHeader(headers, "x-api-key");
|
|
149748
|
+
const userKey = query?.key || getBearerToken(headers) || getStringHeader(headers, "user_key") || getStringHeader(headers, "x-api-key");
|
|
148516
149749
|
const rawDepth = getStringHeader(headers, "x-delegate-depth");
|
|
148517
149750
|
const parsedDepth = rawDepth != null ? parseInt(rawDepth, 10) : void 0;
|
|
148518
149751
|
const delegateDepth = parsedDepth != null && Number.isFinite(parsedDepth) && parsedDepth >= 0 ? Math.min(parsedDepth, MAX_DELEGATE_DEPTH) : void 0;
|
|
@@ -148589,6 +149822,37 @@ function getSessionForRequest(sessionId, sessions) {
|
|
|
148589
149822
|
return session;
|
|
148590
149823
|
}
|
|
148591
149824
|
|
|
149825
|
+
// src/helpers/oauth-challenge.helper.ts
|
|
149826
|
+
function getPublicMcpUrl() {
|
|
149827
|
+
return publicMcpUrl();
|
|
149828
|
+
}
|
|
149829
|
+
function getPublicAuthorizationServerUrl() {
|
|
149830
|
+
return publicAuthorizationServerUrl();
|
|
149831
|
+
}
|
|
149832
|
+
function getProtectedResourceMetadataUrl() {
|
|
149833
|
+
const u2 = new URL(getPublicMcpUrl());
|
|
149834
|
+
const resourcePath = u2.pathname.replace(/\/+$/, "");
|
|
149835
|
+
return `${u2.origin}/.well-known/oauth-protected-resource${resourcePath}`;
|
|
149836
|
+
}
|
|
149837
|
+
function getProtectedResourceMetadata() {
|
|
149838
|
+
return {
|
|
149839
|
+
resource: getPublicMcpUrl(),
|
|
149840
|
+
authorization_servers: [getPublicAuthorizationServerUrl()],
|
|
149841
|
+
scopes_supported: ["mcp"],
|
|
149842
|
+
bearer_methods_supported: ["header"]
|
|
149843
|
+
};
|
|
149844
|
+
}
|
|
149845
|
+
function getOAuthChallengeHeaderValue() {
|
|
149846
|
+
return `Bearer resource_metadata="${getProtectedResourceMetadataUrl()}"`;
|
|
149847
|
+
}
|
|
149848
|
+
function setOAuthChallengeHeader(reply) {
|
|
149849
|
+
reply.header("WWW-Authenticate", getOAuthChallengeHeaderValue());
|
|
149850
|
+
}
|
|
149851
|
+
function sendOAuthDiscoveryChallenge(reply, message = "Authentication required") {
|
|
149852
|
+
setOAuthChallengeHeader(reply);
|
|
149853
|
+
sendJsonRpcError(reply, JsonRpcErrorCode.SERVER_ERROR, message, 401);
|
|
149854
|
+
}
|
|
149855
|
+
|
|
148592
149856
|
// src/helpers/mcp-handlers.helper.ts
|
|
148593
149857
|
var McpRequestHandlers = class {
|
|
148594
149858
|
/**
|
|
@@ -148873,6 +150137,11 @@ var McpSseService = class McpSseService2 extends LifecycleService {
|
|
|
148873
150137
|
if (!this.validateSecurityHeaders(request, reply)) {
|
|
148874
150138
|
return;
|
|
148875
150139
|
}
|
|
150140
|
+
if (!extractAuthHeaders(request).userKey) {
|
|
150141
|
+
this.logger.debug("Unauthenticated /sse request \u2014 returning OAuth discovery challenge");
|
|
150142
|
+
sendOAuthDiscoveryChallenge(reply);
|
|
150143
|
+
return;
|
|
150144
|
+
}
|
|
148876
150145
|
const identity = await authenticateSession(request, this.loggerService, this.natsService, this.runtimeExecutionId);
|
|
148877
150146
|
this.logger.info(`Authenticated SSE connection for skill: ${identity.name}`);
|
|
148878
150147
|
const skillService = await createSkillService(identity, this.loggerService, this.natsService, this.cacheService, this.healthService, this.scriptService, this.delegateService, this.fileService, this.skillContentService, this.staticToolRegistry, this.runtimeCatalogService, this.authService?.getIdentity()?.id ?? void 0);
|
|
@@ -148893,6 +150162,7 @@ var McpSseService = class McpSseService2 extends LifecycleService {
|
|
|
148893
150162
|
} catch (error48) {
|
|
148894
150163
|
this.logger.error({ err: error48 }, "Error handling SSE connection");
|
|
148895
150164
|
if (error48 instanceof Error && error48.message.includes("Authentication failed")) {
|
|
150165
|
+
setOAuthChallengeHeader(reply);
|
|
148896
150166
|
sendJsonRpcError(reply, JsonRpcErrorCode.SERVER_ERROR, error48.message, 401);
|
|
148897
150167
|
} else if (error48 instanceof Error && error48.message.includes("Invalid")) {
|
|
148898
150168
|
sendJsonRpcError(reply, JsonRpcErrorCode.INVALID_REQUEST, error48.message, 400);
|
|
@@ -150291,8 +151561,23 @@ var McpStreamableService = class McpStreamableService2 extends LifecycleService
|
|
|
150291
151561
|
async setupTransport() {
|
|
150292
151562
|
this.logger.info(`Setting up ${this.name} transport`);
|
|
150293
151563
|
this.registerMcpRoute();
|
|
151564
|
+
this.registerProtectedResourceMetadataRoute();
|
|
150294
151565
|
this.logger.info(`${this.name} transport configured`);
|
|
150295
151566
|
}
|
|
151567
|
+
/**
|
|
151568
|
+
* Register the RFC 9728 Protected Resource Metadata endpoint on this MCP
|
|
151569
|
+
* server's own origin. Public + unauthenticated by design. We serve both the
|
|
151570
|
+
* root well-known path and the path-aware variants (`/.well-known/oauth-protected-resource/*`)
|
|
151571
|
+
* so clients that derive the metadata URL from the resource path also resolve.
|
|
151572
|
+
*/
|
|
151573
|
+
registerProtectedResourceMetadataRoute() {
|
|
151574
|
+
const fastify2 = this.fastifyManager.getInstance();
|
|
151575
|
+
const handler = async (_request, reply) => {
|
|
151576
|
+
return reply.header("cache-control", "no-store").send(getProtectedResourceMetadata());
|
|
151577
|
+
};
|
|
151578
|
+
fastify2.get("/.well-known/oauth-protected-resource", handler);
|
|
151579
|
+
fastify2.get("/.well-known/oauth-protected-resource/*", handler);
|
|
151580
|
+
}
|
|
150296
151581
|
/**
|
|
150297
151582
|
* Register the /mcp route handler for streamable HTTP transport.
|
|
150298
151583
|
* Handles GET (listen streams), POST (JSON-RPC messages), and DELETE (session termination).
|
|
@@ -150468,6 +151753,14 @@ var McpStreamableService = class McpStreamableService2 extends LifecycleService
|
|
|
150468
151753
|
* Creates a new session for an authenticated request
|
|
150469
151754
|
*/
|
|
150470
151755
|
async createNewSession(request, reply) {
|
|
151756
|
+
if (!extractAuthHeaders(request).userKey) {
|
|
151757
|
+
this.logger.debug("Unauthenticated /mcp request \u2014 returning OAuth discovery challenge");
|
|
151758
|
+
setOAuthChallengeHeader(reply);
|
|
151759
|
+
return reply.status(401).send({
|
|
151760
|
+
error: "Unauthorized",
|
|
151761
|
+
message: "Authentication required"
|
|
151762
|
+
});
|
|
151763
|
+
}
|
|
150471
151764
|
try {
|
|
150472
151765
|
const identity = await authenticateSession(request, this.loggerService, this.natsService, this.runtimeExecutionId);
|
|
150473
151766
|
this.logger.info(`Authenticated new stream connection for skill: ${identity.name}`);
|
|
@@ -150481,6 +151774,7 @@ var McpStreamableService = class McpStreamableService2 extends LifecycleService
|
|
|
150481
151774
|
const isAuthError = error48 instanceof Error && (msg.includes("unauthorized") || msg.includes("authentication") || msg.includes("forbidden"));
|
|
150482
151775
|
if (isAuthError) {
|
|
150483
151776
|
this.logger.error({ err: error48 }, "Authentication failed");
|
|
151777
|
+
setOAuthChallengeHeader(reply);
|
|
150484
151778
|
return reply.status(401).send({
|
|
150485
151779
|
error: "Unauthorized",
|
|
150486
151780
|
message: `Authentication failed: ${error48 instanceof Error ? error48.message : String(error48)}`
|
|
@@ -156565,10 +157859,9 @@ var start = () => {
|
|
|
156565
157859
|
}
|
|
156566
157860
|
const randomSuffix = (0, import_crypto3.randomBytes)(2).toString("hex");
|
|
156567
157861
|
const runtimeId = "runtime:" + randomSuffix;
|
|
156568
|
-
const natsServers = process.env.NATS_SERVERS || "localhost:4222";
|
|
156569
157862
|
const natsName = process.env.NATS_NAME || runtimeId;
|
|
156570
157863
|
container.bind(NATS_CONNECTION_OPTIONS).toConstantValue({
|
|
156571
|
-
servers: natsServers,
|
|
157864
|
+
servers: natsServers(),
|
|
156572
157865
|
name: natsName,
|
|
156573
157866
|
reconnect: true,
|
|
156574
157867
|
maxReconnectAttempts: -1,
|