@bevel-software/platform-core-backend 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-guide/access-control.md +146 -300
- package/agent-guide/conventions.md +23 -25
- package/agent-guide/directory-structure.md +72 -120
- package/agent-guide/drafts-and-change-requests.md +92 -0
- package/agent-guide/for-git-users.md +47 -0
- package/agent-guide/glossary.md +36 -0
- package/agent-guide/how-agents-find-things.md +49 -0
- package/agent-guide/html-views.md +16 -11
- package/agent-guide/new-knowledge-base.md +9 -8
- package/agent-guide/skills.md +67 -42
- package/agent-guide/start-here.md +65 -0
- package/agent-guide/tool-manuals.md +132 -154
- package/agent-guide/tools.md +67 -0
- package/agent-guide/where-a-new-file-goes.md +45 -28
- package/dist/core/create-core-server.js +1 -1
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +6 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +7 -4
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +22 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +3 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +75 -0
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +23 -4
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/index.d.ts +1 -1
- package/dist/modules/access/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +103 -13
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
- package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.js +45 -31
- package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
- package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
- package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
- package/dist/modules/agent-guide/guide-budget.js +67 -0
- package/dist/modules/agent-guide/guide-budget.js.map +1 -0
- package/dist/modules/agent-guide/index.d.ts +2 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +2 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +4 -0
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +10 -8
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +2 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +1 -38
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +6 -35
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +85 -39
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/oidc-check.d.ts.map +1 -1
- package/dist/modules/settings/oidc-check.js +5 -17
- package/dist/modules/settings/oidc-check.js.map +1 -1
- package/dist/modules/skills/skills.tools.js +1 -1
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +1 -1
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +27 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +12 -8
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +115 -44
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +31 -10
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +22 -18
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
- package/dist/modules/workspace/agent-access.gate.js +1 -1
- package/dist/modules/workspace/agent-access.gate.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +3 -2
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +71 -21
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/oauth-token-error.d.ts +23 -0
- package/dist/shared/oauth-token-error.d.ts.map +1 -0
- package/dist/shared/oauth-token-error.js +47 -0
- package/dist/shared/oauth-token-error.js.map +1 -0
- package/dist/shared/read-capped.d.ts +19 -0
- package/dist/shared/read-capped.d.ts.map +1 -0
- package/dist/shared/read-capped.js +45 -0
- package/dist/shared/read-capped.js.map +1 -0
- package/package.json +4 -3
- package/src/__tests__/hexis-catalog.ts +144 -0
- package/src/__tests__/real-disk.ts +21 -0
- package/src/__tests__/tokens.ts +19 -0
- package/src/__tests__/tool-manuals.ts +32 -0
- package/src/__tests__/tool-wiring.ts +25 -0
- package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
- package/src/core/create-core-server.ts +1 -1
- package/src/core/create-core-services.ts +13 -3
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -12
- package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
- package/src/modules/access/access-control.interface.ts +26 -0
- package/src/modules/access/access-control.service.ts +92 -0
- package/src/modules/access/access.routes.ts +23 -4
- package/src/modules/access/index.ts +1 -1
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +79 -25
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
- package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
- package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
- package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
- package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
- package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +49 -31
- package/src/modules/agent-guide/agent-guide.ts +120 -13
- package/src/modules/agent-guide/guide-budget.ts +73 -0
- package/src/modules/agent-guide/index.ts +12 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +4 -4
- package/src/modules/agent-instructions/compose.ts +4 -0
- package/src/modules/agent-instructions/shared-file-rules.ts +10 -8
- package/src/modules/code-mode/code-mode.tool.ts +2 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
- package/src/modules/connection-probe/connection-probe.service.ts +1 -34
- package/src/modules/mcp/mcp.service.ts +5 -35
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
- package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
- package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
- package/src/modules/secrets-vault/index.ts +1 -1
- package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
- package/src/modules/secrets-vault/secrets-vault.routes.ts +91 -39
- package/src/modules/settings/oidc-check.ts +5 -18
- package/src/modules/skills/skills.tools.ts +1 -1
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
- package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
- package/src/modules/tool-helpers/tool-def.ts +1 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +96 -5
- package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
- package/src/modules/tool-manuals/tool-manuals.contract.ts +22 -0
- package/src/modules/tool-manuals/tool-manuals.service.ts +138 -46
- package/src/modules/tool-manuals/tool-manuals.tools.ts +53 -14
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
- package/src/modules/workflow/agent-tools/workflow.tools.ts +22 -18
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +8 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +156 -49
- package/src/modules/workspace/agent-access.gate.ts +1 -1
- package/src/modules/workspace/workspace.tools.ts +74 -21
- package/src/shared/__tests__/read-capped.test.ts +68 -0
- package/src/shared/oauth-token-error.ts +46 -0
- package/src/shared/read-capped.ts +40 -0
- package/agent-guide/finding-things.md +0 -7
- package/agent-guide/introduction.md +0 -27
package/agent-guide/skills.md
CHANGED
|
@@ -1,55 +1,80 @@
|
|
|
1
1
|
## Skills (`{{skillsDir}}/<scope>/…/<skill>/SKILL.md`, or `{{pluginsDir}}/<Plugin>/skills/<skill>/SKILL.md`)
|
|
2
2
|
|
|
3
3
|
A skill is a folder holding a `SKILL.md` and whatever files it needs. Shared
|
|
4
|
-
skills live under `{{skillsDir}}/`, organised by ownership; a skill that
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
version:
|
|
4
|
+
skills live under `{{skillsDir}}/`, organised by ownership; a skill that
|
|
5
|
+
belongs to one plugin alone may live in that plugin's `skills/` folder. Skill
|
|
6
|
+
names are unique across the catalog. The frontmatter names the skill, says
|
|
7
|
+
when to use it, and may list tools and a version:
|
|
9
8
|
|
|
10
9
|
```yaml
|
|
11
10
|
---
|
|
12
11
|
name: weekly-newsletter
|
|
13
|
-
description:
|
|
12
|
+
description: "Use when someone asks for the Friday newsletter: drafts it for review."
|
|
14
13
|
allowed-tools: [slack_post_message]
|
|
15
14
|
metadata:
|
|
16
15
|
version: "1.4.0"
|
|
17
16
|
---
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
The
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
the
|
|
19
|
+
- **The frontmatter is YAML.** Quote a value that contains `: ` or starts
|
|
20
|
+
with a special character, as above, or the description comes out empty and
|
|
21
|
+
nobody finds the skill. Read it back with `get_skill` after writing.
|
|
22
|
+
- **`description` is how the skill is found**: agents load a skill when its
|
|
23
|
+
description fits the task. Say when to use it, in the words people use.
|
|
24
|
+
- **`allowed-tools`** names the tools the skill uses: any of the platform's
|
|
25
|
+
tools, a plugin's (as `<server>_<tool>`), and the client's own (`Bash`,
|
|
26
|
+
`Read`, `Bash(git:*)`). The platform warns about a name it does not know,
|
|
27
|
+
and enforces nothing.
|
|
28
|
+
- **`metadata.version`** (or a top-level `version`) is reported by
|
|
29
|
+
`list_skills`; `get_skill` with `version` loads the skill as it was when it
|
|
30
|
+
last declared that version. Other `metadata` keys are the author's notes.
|
|
31
|
+
|
|
32
|
+
The body is the instructions, in plain markdown.
|
|
33
|
+
|
|
34
|
+
**A skill on a draft is not live.** The app's skills page, the prompts and
|
|
35
|
+
`list_skills` without `branch` show only the released catalog, read from
|
|
36
|
+
`{{defaultBranch}}`, so a skill on a draft "doesn't exist" there until it is
|
|
37
|
+
merged. To try it, pass the draft: `list_skills({ body: { branch: "<draft>" } })`,
|
|
38
|
+
then `get_skill({ body: { name: "weekly-newsletter", branch: "<draft>" } })`.
|
|
39
|
+
A skill that differs from the released one comes back with `unmerged: true`,
|
|
40
|
+
and its body opens with a line saying it is not approved. Once merged, it is
|
|
41
|
+
in the next `list_skills` or `get_skill`; a client that cached the prompt list
|
|
42
|
+
when it connected must reconnect to show it as a prompt.
|
|
43
|
+
|
|
44
|
+
Before offering to change a skill, check its `canWrite` (see `access-control`).
|
|
45
|
+
|
|
46
|
+
**How skills reach agents.** Through this server (prompts, `list_skills`,
|
|
47
|
+
`get_skill`), or as native plugins: each person can install a plugin
|
|
48
|
+
marketplace from the app's external agent page, compiled from exactly the
|
|
49
|
+
skills they may read.
|
|
50
|
+
|
|
51
|
+
### A skill that needs tools checks them first
|
|
52
|
+
|
|
53
|
+
A skill that calls another service fails for anyone who has not signed in, or
|
|
54
|
+
before the tool is merged. Make its first step a check, and stop with a plain
|
|
55
|
+
instruction rather than a failed call:
|
|
56
|
+
|
|
57
|
+
```markdown
|
|
58
|
+
## Before you start
|
|
59
|
+
Call `list_tool_setup`. Find the Jira tool. If a variable with `scope: user`
|
|
60
|
+
shows `userConfigured: false` (or `authorized: false` for a sign-in), stop and
|
|
61
|
+
say: "Open the Connect page in <app> and sign in to Jira, then ask me again."
|
|
62
|
+
If the tool is missing altogether, say it is not available to them yet and who
|
|
63
|
+
to ask.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### A guided skill for people who are not technical
|
|
67
|
+
|
|
68
|
+
When a skill is for someone who is not an engineer, such as turning an idea
|
|
69
|
+
into a ticket, write it to lead them:
|
|
70
|
+
|
|
71
|
+
- **One question per message**, the most important first. Never a list of
|
|
72
|
+
five.
|
|
73
|
+
- **Propose, then ask them to confirm.** "I would call it 'Search: filter by
|
|
74
|
+
date'. Is that right?" is easier to answer than "What is the title?".
|
|
75
|
+
- **A checklist for "specified enough"** in the skill (who it is for, what
|
|
76
|
+
should happen, how we know it works), and stop asking once it is met.
|
|
77
|
+
- **Plain words.** No platform terms (branch, change request, frontmatter,
|
|
78
|
+
tool names); show the result in their language before you create anything.
|
|
79
|
+
- **Check its tools first** (above), so the conversation does not end in an
|
|
80
|
+
error after ten questions.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
## Start here
|
|
2
|
+
|
|
3
|
+
You are connected to this organisation's knowledge base: its documents, and
|
|
4
|
+
the skills and tools its teams have approved, with access set per folder. To
|
|
5
|
+
the people you work with it is not a Git repository: say "knowledge base",
|
|
6
|
+
"draft" and "change request", and do not explain it as "a Git repo". If they
|
|
7
|
+
ask how it compares, see `for-git-users`.
|
|
8
|
+
|
|
9
|
+
**Read this once per conversation.** Then fetch only the section a task needs,
|
|
10
|
+
`get_agent_guide({ body: { section: "<id>" } })`, and do not reload the whole
|
|
11
|
+
guide. When you start a sub-agent, pass it the rules below and the one section
|
|
12
|
+
its task needs.
|
|
13
|
+
|
|
14
|
+
### Before your first call
|
|
15
|
+
|
|
16
|
+
- **Every file tool takes `branch`.** `{{defaultBranch}}` is the default
|
|
17
|
+
branch: what is live for everyone. Work in progress lives on a draft branch.
|
|
18
|
+
For example:
|
|
19
|
+
`read_file({ body: { branch: "{{defaultBranch}}", path: "{{kbDirName}}/{{knowledgeBaseDir}}/Overview.md" } })`.
|
|
20
|
+
- **Copy each tool's `Call:` line.** It is generated from the tool's own
|
|
21
|
+
schema and shows the shape that tool takes, with every required argument;
|
|
22
|
+
optional ones are in the schema. A call with a missing or unknown argument
|
|
23
|
+
is refused, and the refusal shows the tool's interface.
|
|
24
|
+
- **Pass `sessionId` only where a tool lists it.** Over MCP, call
|
|
25
|
+
`start_session` once and pass its id to the tools whose arguments include
|
|
26
|
+
`sessionId`. Other tools refuse it.
|
|
27
|
+
- **`grep` takes RE2-style regular expressions** (ripgrep): no look-around
|
|
28
|
+
and no backreferences.
|
|
29
|
+
- **Paths sit under `{{kbDirName}}/`**, as in
|
|
30
|
+
`{{kbDirName}}/{{knowledgeBaseDir}}/Overview.md`. A path without that prefix
|
|
31
|
+
is placed under it.
|
|
32
|
+
|
|
33
|
+
### What is live, and what is only on a draft
|
|
34
|
+
|
|
35
|
+
Work on a draft is visible to you, and to anyone who opens that draft in the
|
|
36
|
+
app: its change request, once you open one, shows every changed file.
|
|
37
|
+
**Skills and tools go live only once they are on `{{defaultBranch}}`**,
|
|
38
|
+
merged through a change request or written there directly by someone allowed
|
|
39
|
+
to. Until then the app's skills page, `list_skills` without `branch` and the
|
|
40
|
+
tool list do not show them, and "doesn't exist" means "not merged yet". To see
|
|
41
|
+
a skill on a draft, pass the draft as `branch` to `list_skills` or
|
|
42
|
+
`get_skill`.
|
|
43
|
+
|
|
44
|
+
### Which section answers what
|
|
45
|
+
|
|
46
|
+
| Question | Section |
|
|
47
|
+
|---|---|
|
|
48
|
+
| What does my agent see when it connects, and how does it find the rest? | `how-agents-find-things` |
|
|
49
|
+
| How is this like Git, and how is it not? Why can't I delete this? | `for-git-users` |
|
|
50
|
+
| What is a plugin, a skill, a tool, a secret, a role, a group? | `glossary` |
|
|
51
|
+
| What do the platform's folders hold? | `directory-structure` |
|
|
52
|
+
| Where does a new file go? How do I lay out a team's first project? | `where-a-new-file-goes` |
|
|
53
|
+
| What may the file tools do: content kinds, write modes, moves, deletes? | `working-with-files` |
|
|
54
|
+
| Who may read or change what: roles, groups, `access.md`? | `access-control` |
|
|
55
|
+
| How do I propose, sync or delete a draft, or settle a conflict? | `drafts-and-change-requests` |
|
|
56
|
+
| How do I write a skill, or one for people who are not technical? | `skills` |
|
|
57
|
+
| When can I use a tool, and what does it need set up? | `tools` |
|
|
58
|
+
| How do I add a tool: `.tool` files, `mcp.json`, sign-ins? | `tool-manuals` |
|
|
59
|
+
| How do I build an interactive HTML page? | `html-views` |
|
|
60
|
+
| How are pages named, linked and sourced? | `conventions` |
|
|
61
|
+
| The knowledge base is empty: what now? | `new-knowledge-base` |
|
|
62
|
+
|
|
63
|
+
A deployment may add sections of its own: `sections` in this answer lists
|
|
64
|
+
every one. `get_agent_guide({ body: { section: "all" } })` returns the whole
|
|
65
|
+
guide.
|
|
@@ -1,114 +1,95 @@
|
|
|
1
1
|
## Tool Manuals (`{{pluginsDir}}/<Plugin>/software.bevel.hexis/tools/*.tool`)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
tool that calls another service takes them flat — so every tool description
|
|
16
|
-
opens with one generated line showing how that tool is called, e.g.
|
|
17
|
-
`Call: KNOWLEDGE_BASE.read_file({ body: { branch: "...", path: "..." } })` or
|
|
18
|
-
`Call: SERPER.search({ query: "..." })`. Copy that line and fill it in, inside
|
|
19
|
-
`call_tool_chain` or directly. Arguments that do not match a tool's input
|
|
20
|
-
schema are refused before anything is sent, and the refusal lists what is
|
|
21
|
-
wrong and gives the tool's whole interface.
|
|
22
|
-
|
|
23
|
-
A `.tool` file is JSON or YAML. Its `type` decides how tools are discovered:
|
|
24
|
-
|
|
25
|
-
- **`inline`** — the tools are embedded in the file (no network round-trip to list them).
|
|
26
|
-
- **`http`** — `url` points to an endpoint that returns a UTCP manual.
|
|
27
|
-
|
|
28
|
-
(`type: mcp` is the LEGACY spelling of an MCP server as a `.tool`. The boot
|
|
29
|
-
migration converts such files into `mcp.json` entries; do not write new ones.)
|
|
30
|
-
|
|
31
|
-
**The tool is the frontmatter.** A `.tool` is one `---` YAML block holding *everything* — its `id`, its access verbs (`read:`/`write:`/`owner:`/`download:`), and its config (`type`/`url`/`variables`/…) — all in the same object. Anything after the closing `---` is free-form notes the parser ignores (like a `SKILL.md` body):
|
|
3
|
+
A `.tool` file is a **tool manual**: it lets agents call a service's HTTP API.
|
|
4
|
+
It lives in a plugin's `software.bevel.hexis/tools/` folder, beside the skills
|
|
5
|
+
that use it. `.tool` files are for `http` and `inline` manuals only: an MCP
|
|
6
|
+
server belongs in the plugin's `mcp.json` (see `directory-structure`). Who may
|
|
7
|
+
use and who manages a tool is in `tools`.
|
|
8
|
+
|
|
9
|
+
**The tool is the frontmatter.** A `.tool` is one `---` YAML block holding
|
|
10
|
+
everything: its `id`, its access verbs (`read`/`write`/`owner`/`download`,
|
|
11
|
+
which override the folder's `access.md` for this file) and its config
|
|
12
|
+
(`type`, `url`, `variables`, …). Anything after the closing `---` is notes the
|
|
13
|
+
parser ignores. A file without the fence is the older form: the whole file is
|
|
14
|
+
the object, so a bare JSON `.tool` still works.
|
|
32
15
|
|
|
33
16
|
```yaml
|
|
34
17
|
---
|
|
35
18
|
id: my_tool
|
|
36
19
|
write:
|
|
37
20
|
- group/Product Team
|
|
38
|
-
owner:
|
|
39
|
-
- Jane Doe <jane@x.com>
|
|
40
21
|
type: http
|
|
41
22
|
url: https://api.example.com/utcp
|
|
42
23
|
---
|
|
43
24
|
```
|
|
44
25
|
|
|
45
|
-
|
|
26
|
+
`type` decides how the tools are found: **`inline`** embeds them in the file,
|
|
27
|
+
**`http`** points `url` at an endpoint that returns a UTCP manual.
|
|
28
|
+
(`type: mcp` is an older way to declare an MCP server; startup moves such files
|
|
29
|
+
into `mcp.json` where it can. Do not write new ones.)
|
|
46
30
|
|
|
47
|
-
**`id`
|
|
31
|
+
**`id` is the manual's identity**: the namespace its secrets bind to and its
|
|
32
|
+
address. Use lowercase `snake_case`. Without an `id`, the `name`, then the file
|
|
33
|
+
name, is used. It must be unique among all `.tool` files and `mcp.json` server
|
|
34
|
+
names, and names differing only in `-` and `_` collide: the second is refused
|
|
35
|
+
(an `mcp.json` server wins over a `.tool`), not renamed, and `list_tool_setup`
|
|
36
|
+
names it under `invalid`. The same service in two plugins needs two ids
|
|
37
|
+
(`serper_everyone`, `serper_finance`), each with its own secrets and access.
|
|
48
38
|
|
|
49
|
-
|
|
39
|
+
### Secrets: `${VAR}` and `variables`
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
Wherever a manual needs a credential, write a placeholder such as
|
|
42
|
+
`${API_KEY}`. At call time it is filled from the platform's Secrets Vault,
|
|
43
|
+
under a key made from the manual's id and the variable's name, so a secret
|
|
44
|
+
belongs to one manual. The app's Secrets page shows the exact key; do not spell
|
|
45
|
+
it by hand.
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
Declare who provides each variable in an optional `variables` list of
|
|
48
|
+
`{ name, scope, label? }`:
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
It serves everything the hosted endpoint serves **plus** the local-only tools, because it runs on the machine where they exist. Remote tools still execute on the server, so their shared keys and OAuth sign-ins keep working untouched; a local-only tool's own `${VAR}`s are fetched by `hexis-mcp` from this workspace's Secrets Vault with the connection key, and from the environment of whatever launched the command (your MCP client's config) for any the vault does not hold. Reading the `.tool` and wiring the server into your client by hand still works and is the fallback when the command is unavailable.
|
|
60
|
-
|
|
61
|
-
### Referencing secrets — `${VAR}` and the `variables` block
|
|
62
|
-
|
|
63
|
-
Anywhere a `.tool` needs a credential (an API key, a token) write a placeholder like `${API_KEY}`. At call time it is filled from the **Secrets Vault** under a key derived from the manual's resolved id (the same `id` → `name` → file-name resolution described above) and the variable's name: for a plain id the key is `<id>_<VAR>` — a manual whose id is `weather` referencing `${API_KEY}` reads the secret `weather_API_KEY` — while an id that contains `_` or punctuation is namespaced with every such character turned into `_` and then doubled (`serper_everyone` + `API_KEY` → `serper__everyone_API_KEY`). `list_tool_setup` and the Secrets page show the exact key each manual reads, so read it there rather than spelling it by hand. A secret is therefore bound to exactly one manual; another manual cannot read it.
|
|
50
|
+
- **`scope: admin`** (the default): set once by a writer of the file and shared
|
|
51
|
+
by everyone who uses the tool. Prefer it.
|
|
52
|
+
- **`scope: user`**: each person sets their own.
|
|
64
53
|
|
|
65
|
-
|
|
54
|
+
`name` matches `[A-Za-z0-9_]+`. A `${VAR}` you do not declare is `admin` and
|
|
55
|
+
still shows in the app, so the list is needed only for `user` scope, a label,
|
|
56
|
+
or a sign-in. Values are entered in the app, never in the file. A malformed
|
|
57
|
+
entry makes the whole file fail to load (it then shows under `invalid`).
|
|
66
58
|
|
|
67
|
-
-
|
|
68
|
-
- **`scope: user`** — set by **each end user** for themselves (their own value, never shared).
|
|
59
|
+
### Sign-ins: the `oauth` block
|
|
69
60
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
A `user`-scoped variable can be filled by **signing in** instead of by a typed value: add an `oauth` block and each member authorizes with the provider; the token then rides in whatever header references `${VAR}`. The block carries PUBLIC config only:
|
|
61
|
+
A `user` variable can be filled by signing in instead of typing a value: add
|
|
62
|
+
an `oauth` block, and the token rides in whatever header uses `${VAR}`. The
|
|
63
|
+
block carries public settings only:
|
|
75
64
|
|
|
76
65
|
| field | | |
|
|
77
66
|
|---|---|---|
|
|
78
|
-
| `clientId` | required | the OAuth app's client id
|
|
79
|
-
| `authorizationUrl`, `tokenUrl` |
|
|
80
|
-
| `scopes` | optional | `string[]`, requested at sign-in
|
|
81
|
-
| `pkce` | optional, default
|
|
82
|
-
| `resource` | optional | RFC 8707 resource indicator
|
|
83
|
-
| `authParams` | optional | extra
|
|
84
|
-
|
|
85
|
-
**Never** a `clientSecret
|
|
86
|
-
|
|
87
|
-
For an `mcp.json` server the declaration
|
|
67
|
+
| `clientId` | required | the OAuth app's client id; its owner registers the app with the redirect URI `<backend>/api/secrets/oauth/callback` |
|
|
68
|
+
| `authorizationUrl`, `tokenUrl` | required in a `.tool`; optional on an `mcp.json` server | an MCP server's are discovered from its own OAuth metadata, so give them only when that discovery fails. Both or neither |
|
|
69
|
+
| `scopes` | optional | `string[]`, requested at sign-in |
|
|
70
|
+
| `pkce` | optional, default on | only `false` means anything |
|
|
71
|
+
| `resource` | optional | the RFC 8707 resource indicator; discovered on an `mcp.json` server |
|
|
72
|
+
| `authParams` | optional | extra fixed parameters, such as Google's `access_type: offline` |
|
|
73
|
+
|
|
74
|
+
**Never** a `clientSecret`: a `.tool` with one fails to load, and an `mcp.json`
|
|
75
|
+
server whose entry has one is dropped. The secret is pasted once on the tool's
|
|
76
|
+
page. For an `mcp.json` server, the declaration goes in `plugin.json`, beside
|
|
77
|
+
the header that uses it:
|
|
88
78
|
|
|
89
79
|
```json
|
|
90
|
-
{
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"headers": { "Authorization": "Bearer ${HUBSPOT_TOKEN}" },
|
|
96
|
-
"variables": [
|
|
97
|
-
{ "name": "HUBSPOT_TOKEN", "scope": "user", "label": "HubSpot sign-in",
|
|
98
|
-
"oauth": { "clientId": "<the app's client id>" } }
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
80
|
+
{ "extensions": { "software.bevel.hexis": { "mcpServers": { "hubspot": {
|
|
81
|
+
"headers": { "Authorization": "Bearer ${HUBSPOT_TOKEN}" },
|
|
82
|
+
"variables": [{ "name": "HUBSPOT_TOKEN", "scope": "user", "label": "HubSpot sign-in",
|
|
83
|
+
"oauth": { "clientId": "<the app's client id>" } }]
|
|
84
|
+
} } } } }
|
|
105
85
|
```
|
|
106
86
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### Calling a Google API as a service account: `auth_type: google_service_account`
|
|
87
|
+
### Google APIs as a service account
|
|
110
88
|
|
|
111
|
-
Some Google APIs (
|
|
89
|
+
Some Google APIs (Ads, Tag Manager, BigQuery, Sheets) are called as one shared
|
|
90
|
+
service account, with a short-lived token minted from its key. Name the key in
|
|
91
|
+
an `auth` block on an inline tool's `tool_call_template` (with
|
|
92
|
+
`call_template_type: http`), and the platform mints, caches and sends the token:
|
|
112
93
|
|
|
113
94
|
```yaml
|
|
114
95
|
---
|
|
@@ -134,94 +115,91 @@ tools:
|
|
|
134
115
|
---
|
|
135
116
|
```
|
|
136
117
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
The token always comes from Google's own token endpoint; a `token_uri` inside the key is ignored. Give the service account access in the Google product itself (for example add its email as a user of the Google Ads account or the Tag Manager container), or that product refuses every call with an error of its own. When Google refuses the key itself (a revoked key, a scope the account may not have, a subject without delegation), the error names the service account and Google's reason, never the key.
|
|
144
|
-
|
|
145
|
-
The block works in one place: an inline tool's `tool_call_template` with `call_template_type: http`. Anywhere else (an `sse`, `streamable_http` or `mcp` template, or a `type: http` / `type: mcp` tool that discovers its tools from a `url`) no token would be sent, so the `.tool` is refused and `list_tool_setup` names it under `invalid`, saying where the block was found. It works the same for a `remote: false` tool run by the local `hexis-mcp` server, which mints the token on the machine it runs on.
|
|
146
|
-
|
|
147
|
-
A service account is one shared identity. To have each person call Google as themselves instead, do not use this block: declare a sign-in variable (`oauth`, above) and send it as `Authorization: Bearer ${VAR}`.
|
|
118
|
+
`credentials` is always a `${VAR}` holding the key JSON (whole, or base64);
|
|
119
|
+
`scopes` is required; `subject` (optional) is a user to act as, for a service
|
|
120
|
+
account with domain-wide delegation. The block anywhere else is refused and
|
|
121
|
+
named under `invalid`. Give the service account access in the Google product
|
|
122
|
+
itself. To have each person call Google as themselves, use a sign-in
|
|
123
|
+
(`oauth`) instead.
|
|
148
124
|
|
|
149
125
|
### Examples
|
|
150
126
|
|
|
151
|
-
An `http` manual
|
|
127
|
+
An `http` manual with a shared and a personal key:
|
|
152
128
|
|
|
153
129
|
```yaml
|
|
154
|
-
|
|
130
|
+
---
|
|
131
|
+
id: weather
|
|
155
132
|
type: http
|
|
156
133
|
url: https://api.weather.example/utcp
|
|
157
134
|
headers:
|
|
158
135
|
Authorization: Bearer ${ORG_KEY}
|
|
159
136
|
X-User-Key: ${USER_KEY}
|
|
160
137
|
variables:
|
|
161
|
-
- { name: ORG_KEY, scope: admin, label: "Org-wide weather
|
|
162
|
-
- { name: USER_KEY, scope: user, label: "Your personal weather
|
|
138
|
+
- { name: ORG_KEY, scope: admin, label: "Org-wide weather key" }
|
|
139
|
+
- { name: USER_KEY, scope: user, label: "Your personal weather key" }
|
|
140
|
+
---
|
|
163
141
|
```
|
|
164
142
|
|
|
165
143
|
An `inline` manual with one tool:
|
|
166
144
|
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
}
|
|
145
|
+
```yaml
|
|
146
|
+
---
|
|
147
|
+
id: billing
|
|
148
|
+
type: inline
|
|
149
|
+
variables:
|
|
150
|
+
- { name: BILLING_KEY, scope: admin }
|
|
151
|
+
tools:
|
|
152
|
+
- name: create_invoice
|
|
153
|
+
description: Create an invoice.
|
|
154
|
+
inputs: { type: object, properties: {} }
|
|
155
|
+
outputs: { type: object, properties: {} }
|
|
156
|
+
tool_call_template:
|
|
157
|
+
call_template_type: http
|
|
158
|
+
http_method: POST
|
|
159
|
+
url: https://api.billing.example/invoices
|
|
160
|
+
headers: { Authorization: "Bearer ${BILLING_KEY}" }
|
|
161
|
+
---
|
|
187
162
|
```
|
|
188
163
|
|
|
189
164
|
### Adding a third-party tool
|
|
190
165
|
|
|
191
|
-
When asked to add
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
**
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
3.
|
|
226
|
-
|
|
227
|
-
|
|
166
|
+
When asked to add a product as a tool ("add Notion", "wire up Linear"), never
|
|
167
|
+
invent an endpoint or write a placeholder URL:
|
|
168
|
+
|
|
169
|
+
1. **Find the real endpoint in the vendor's own docs**, preferring their
|
|
170
|
+
official remote MCP server, else their REST API. Confirm the URL, transport
|
|
171
|
+
and auth with a web search, not from memory; without web access, ask the
|
|
172
|
+
person.
|
|
173
|
+
2. **Pick the home.** An MCP server is an entry in the plugin's `mcp.json`
|
|
174
|
+
(`type: "streamable-http"` and the `https://` URL, never `ws://`). A REST
|
|
175
|
+
API is a `.tool` with `type: http`, or `type: inline` when you write each
|
|
176
|
+
call by hand.
|
|
177
|
+
3. **An OAuth-protected MCP server usually needs only its `mcp.json` entry.**
|
|
178
|
+
The app probes it, registers itself and offers a sign-in on the Connect page
|
|
179
|
+
(`oauth-auto`). An `Authorization` header or an `oauth` variable turns that
|
|
180
|
+
discovery off, so add neither. Some providers do not support it
|
|
181
|
+
(`oauth-manual`, such as HubSpot or Google): write the entry; once it is
|
|
182
|
+
live, `list_tool_setup` answers `setup.kind` and `setup.reason` for it.
|
|
183
|
+
4. **For a key, use `variables`**, never a key in the file:
|
|
184
|
+
`Authorization: Bearer ${NOTION_TOKEN}` and a `variables` entry with its
|
|
185
|
+
scope.
|
|
186
|
+
5. **Mark tools that only work on the person's machine**: `local: true` on an
|
|
187
|
+
MCP server's `plugin.json` entry, `remote: false` on a `.tool`.
|
|
188
|
+
|
|
189
|
+
All of it lands on a draft first, and goes live once merged (see `tools`).
|
|
190
|
+
|
|
191
|
+
**Walking an admin through `oauth-manual`:**
|
|
192
|
+
|
|
193
|
+
1. Register an OAuth app with the provider, with the redirect URI that
|
|
194
|
+
`setup.reason` gives.
|
|
195
|
+
2. Ask for the app's **client id** (public, fine in the chat) and write the
|
|
196
|
+
declaration: for an `mcp.json` server, the `variables` entry with
|
|
197
|
+
`oauth: { clientId }` and the `Authorization: Bearer ${VAR}` header in its
|
|
198
|
+
`plugin.json` entry; for a `.tool`, the same with `authorizationUrl` and
|
|
199
|
+
`tokenUrl`. A person can do the same under "Edit server" on the tool's page.
|
|
200
|
+
3. Once the declaration is on `{{defaultBranch}}` (a draft's entry shows in
|
|
201
|
+
`onBranchOnly` with no `setup`), run `list_tool_setup`: `setup.reason` must
|
|
202
|
+
be gone. If the endpoints could not be discovered, add `authorizationUrl`
|
|
203
|
+
and `tokenUrl` from the provider's docs.
|
|
204
|
+
4. The admin pastes the **client secret** into the tool's page, never into
|
|
205
|
+
the file or the chat. Then everyone signs in on the Connect page.
|