@cat-factory/mcp-server 0.2.1 → 0.3.1
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/README.md +123 -28
- package/dist/bin.js +11 -19
- package/dist/bin.js.map +1 -1
- package/dist/config.d.ts +29 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +102 -10
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +66 -5
- package/dist/instructions.js.map +1 -1
- package/dist/result.d.ts +7 -0
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +66 -12
- package/dist/result.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +12 -5
- package/dist/server.js.map +1 -1
- package/dist/stdio.d.ts +38 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +31 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tools.generated.d.ts +22 -0
- package/dist/tools.generated.d.ts.map +1 -1
- package/dist/tools.generated.js +43 -2
- package/dist/tools.generated.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,6 +10,17 @@ It is a **facade**, not a client. Every tool is one call on
|
|
|
10
10
|
|
|
11
11
|
## Run it
|
|
12
12
|
|
|
13
|
+
With Claude Code, in one line:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
claude mcp add cat-factory \
|
|
17
|
+
--env CAT_FACTORY_BASE_URL=https://cat-factory.example.com \
|
|
18
|
+
--env CAT_FACTORY_API_KEY_FILE=$HOME/.config/cat-factory/api-key \
|
|
19
|
+
-- npx -y @cat-factory/mcp-server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or in any host's own config format:
|
|
23
|
+
|
|
13
24
|
```jsonc
|
|
14
25
|
// An MCP host's server config
|
|
15
26
|
{
|
|
@@ -19,7 +30,7 @@ It is a **facade**, not a client. Every tool is one call on
|
|
|
19
30
|
"args": ["-y", "@cat-factory/mcp-server"],
|
|
20
31
|
"env": {
|
|
21
32
|
"CAT_FACTORY_BASE_URL": "https://cat-factory.example.com",
|
|
22
|
-
"
|
|
33
|
+
"CAT_FACTORY_API_KEY_FILE": "/home/you/.config/cat-factory/api-key",
|
|
23
34
|
},
|
|
24
35
|
},
|
|
25
36
|
},
|
|
@@ -30,19 +41,76 @@ Mint the key from the deployment (`backend/docs/public-api.md` §Setup) and give
|
|
|
30
41
|
scope that does the job**: `read ⊂ write ⊂ decide ⊂ admin`. Every tool is scoped to the key's
|
|
31
42
|
workspace, and the key is what actually decides what a model can do here.
|
|
32
43
|
|
|
33
|
-
| Variable | Meaning
|
|
34
|
-
| ---------------------------------- |
|
|
35
|
-
| `CAT_FACTORY_BASE_URL` | The deployment's origin. Required.
|
|
36
|
-
| `CAT_FACTORY_API_KEY` | A public-API key. Required.
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
| Variable | Meaning |
|
|
45
|
+
| ---------------------------------- | -------------------------------------------------------------------------- |
|
|
46
|
+
| `CAT_FACTORY_BASE_URL` | The deployment's origin. Required. |
|
|
47
|
+
| `CAT_FACTORY_API_KEY` | A public-API key. Required, unless the file below is given. |
|
|
48
|
+
| `CAT_FACTORY_API_KEY_FILE` | A file holding the key instead. Either one, never both. |
|
|
49
|
+
| `CAT_FACTORY_MCP_GROUPS` | Comma-separated resource groups to expose. Unset ⇒ all of them. |
|
|
50
|
+
| `CAT_FACTORY_MCP_TOOLS` | Comma-separated tool names to expose. Unset ⇒ all the other filters leave. |
|
|
51
|
+
| `CAT_FACTORY_MCP_EXCLUDE_TOOLS` | Comma-separated tool names to withhold, applied after every other filter. |
|
|
52
|
+
| `CAT_FACTORY_MCP_READ_ONLY` | `true` ⇒ expose only the tools that change nothing. |
|
|
53
|
+
| `CAT_FACTORY_MCP_MAX_RESULT_CHARS` | Ceiling on one tool result. Default 100,000. |
|
|
54
|
+
| `CAT_FACTORY_MCP_TIMEOUT_MS` | Per-request deadline passed to the SDK. `0` disables it. |
|
|
55
|
+
| `CAT_FACTORY_MCP_MAX_RETRIES` | Retries for a retriable failure, passed to the SDK. |
|
|
56
|
+
|
|
57
|
+
Missing credentials, an unknown group or tool name, a filter combination that would expose nothing,
|
|
58
|
+
and a non-numeric ceiling all **fail at startup**. A server that comes up and then fails every call
|
|
59
|
+
is reported by the host as connected, and the model spends turns discovering otherwise.
|
|
60
|
+
|
|
61
|
+
### Keep the key out of the host's config
|
|
62
|
+
|
|
63
|
+
`CAT_FACTORY_API_KEY_FILE` names a file to read the key from. A stdio server's environment IS the
|
|
64
|
+
host's config file, so the inline variable means a long-lived credential in plaintext in a home
|
|
65
|
+
directory, readable by everything that can read that file and present in every backup and every
|
|
66
|
+
screen share of it. A path is not a secret, so pointing at one lets the key live somewhere locked
|
|
67
|
+
down (`chmod 600`, a mounted secret, a secrets-manager sidecar's drop).
|
|
68
|
+
|
|
69
|
+
Setting **both** is refused rather than resolved by precedence: two live sources for one credential
|
|
70
|
+
means a rotation can land on the half nobody reads, and the deployment goes on working with the old
|
|
71
|
+
key right up until it is revoked.
|
|
72
|
+
|
|
73
|
+
### Choosing what a model can reach
|
|
74
|
+
|
|
75
|
+
Three filters, narrowest last, all of them a convenience rather than a boundary (the key's scope is
|
|
76
|
+
the boundary):
|
|
77
|
+
|
|
78
|
+
- `CAT_FACTORY_MCP_GROUPS` is the coarse unit an operator thinks in: "no debug tools on this one".
|
|
79
|
+
- `CAT_FACTORY_MCP_TOOLS` exposes an explicitly chosen set. Precise, but it has to be re-edited
|
|
80
|
+
whenever `/api/v1` grows, and a forgotten edit silently withholds the new capability.
|
|
81
|
+
- `CAT_FACTORY_MCP_EXCLUDE_TOOLS` withholds named tools and keeps admitting everything else,
|
|
82
|
+
including tools added later. This is the one to reach for to keep ONE capability away from a model:
|
|
83
|
+
`CAT_FACTORY_MCP_EXCLUDE_TOOLS=notifications_act` keeps the PR-merging tool away without costing
|
|
84
|
+
the inbox it belongs to.
|
|
85
|
+
|
|
86
|
+
Whatever is switched off, the server **says so in its instructions**, naming the withheld tools and
|
|
87
|
+
stating that the deployment still supports them. An unexplained absence reads to a model as a
|
|
88
|
+
platform that cannot do the thing, which it then reports to its user, or works around.
|
|
89
|
+
|
|
90
|
+
## A worked flow
|
|
91
|
+
|
|
92
|
+
Create a task, run it, watch it, answer the park. This is the shape of nearly every session here:
|
|
93
|
+
|
|
94
|
+
1. **`services_list`**: the board's service frames. A task is created under one, and the `serviceId`
|
|
95
|
+
comes from here rather than being guessable.
|
|
96
|
+
2. **`pipelines_list`**: which pipelines a task can be started with, and which are headless-startable.
|
|
97
|
+
3. **`tasks_create`** (`serviceId`, `body.title`, `body.description`, `body.taskType`) returns the
|
|
98
|
+
`taskId`. Nothing runs yet; this is a board card.
|
|
99
|
+
4. **`tasks_start`** (`taskId`) **spends**: it begins a real agent run against a real repository, and
|
|
100
|
+
returns the `runId`. Confirm with the person first; the tool is annotated `destructiveHint`, so
|
|
101
|
+
most hosts will ask anyway.
|
|
102
|
+
5. **`tasks_get_run`** (`runId`): poll it. There is no streaming tool (see below), an agent step takes
|
|
103
|
+
minutes, so poll every 15-30 seconds and say so instead of going quiet. Keep going until the
|
|
104
|
+
status is terminal or a decision is parked.
|
|
105
|
+
6. **`decisions_list`** (`runId`): a run that stops advancing has usually PARKED on a human decision,
|
|
106
|
+
and it waits indefinitely by design. Answer it with the other `decisions_*` tools
|
|
107
|
+
(`decisions_incorporate`, `decisions_proceed`, `decisions_choose_fork`,
|
|
108
|
+
`decisions_resolve_judge`, …) or leave it for a person.
|
|
109
|
+
7. **`notifications_list`**: the human-actionable tail, including the merge decision.
|
|
110
|
+
`notifications_act` can merge a pull request, which is the other tool that spends.
|
|
111
|
+
|
|
112
|
+
For a run against a supplied brief with no board card and nothing pushed to a repository, the
|
|
113
|
+
`jobs_*` group is the same loop in one step: `jobs_create` → poll `jobs_get` → `jobs_cancel`.
|
|
46
114
|
|
|
47
115
|
To mount it on your own transport instead of stdio:
|
|
48
116
|
|
|
@@ -69,6 +137,15 @@ Concretely:
|
|
|
69
137
|
contract the deployment validates against, so a tool cannot describe a request shape the server
|
|
70
138
|
would reject. An OPEN vocabulary (a closed set plus a documented escape hatch, like `taskType`)
|
|
71
139
|
is never narrowed to an `enum`, because that would refuse a value the server accepts.
|
|
140
|
+
- **The output schema is deliberately looser than the spec.** Every tool that answers with a JSON
|
|
141
|
+
object declares an `outputSchema` and returns `structuredContent` beside the text. But a caller's
|
|
142
|
+
own MCP client VALIDATES a result against that schema, and `/api/v1` is additive forever, so the
|
|
143
|
+
emitted version carries no `required`, no `enum`, no closed `anyOf` and no length or range bounds.
|
|
144
|
+
Each of those would be a way for an older copy of this package to reject a newer deployment's
|
|
145
|
+
honest answer. The known members of a vocabulary are stated in the field's description instead,
|
|
146
|
+
where a new member cannot invalidate them, and a UNION asserts nothing at all beyond its
|
|
147
|
+
discriminator in prose: not even `type`, since the variants a union gains later need not be
|
|
148
|
+
objects.
|
|
72
149
|
- **Argument validation stops at the door.** Required path ids are checked so a request can be
|
|
73
150
|
built at all; the body goes to the deployment, whose 422 names the field and is far more useful
|
|
74
151
|
to a model than anything this layer could say.
|
|
@@ -107,18 +184,27 @@ quietly stale.
|
|
|
107
184
|
|
|
108
185
|
## Things a caller should know
|
|
109
186
|
|
|
110
|
-
- **`readOnlyHint` is set from the HTTP method
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
187
|
+
- **`readOnlyHint` is set from the HTTP method.** `destructiveHint` and `idempotentHint` are set on
|
|
188
|
+
the operations whose consequence is real money or a merged pull request (`tasks_start`,
|
|
189
|
+
`tasks_retry`, `jobs_create`, `notifications_act`, `tasks_delete`) and left UNSET everywhere else.
|
|
190
|
+
That asymmetry is deliberate: the protocol's default for an unset hint is already the cautious one,
|
|
191
|
+
so a blanket `destructiveHint: false` over the cheap writes would lower a host's caution on a
|
|
192
|
+
guess. `tasks_delete` is the case `readOnlyHint` cannot express at all: idempotent AND
|
|
193
|
+
destructive.
|
|
114
194
|
- **Read-only mode is a convenience, not a boundary.** It removes tools from this server; the key
|
|
115
|
-
still carries whatever scope it was minted with. Mint a `read`-scoped key for the boundary.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
195
|
+
still carries whatever scope it was minted with. Mint a `read`-scoped key for the boundary. The
|
|
196
|
+
same goes for the two per-tool filters.
|
|
197
|
+
- **A result that does not fit is REFUSED, not truncated.** The message names how many characters
|
|
198
|
+
there were, the limit, and the way out from either side: `limit` / `cursor` on the list endpoints,
|
|
199
|
+
`offset` on the debug text reads, or a bigger `CAT_FACTORY_MCP_MAX_RESULT_CHARS`. Half an object
|
|
200
|
+
cannot satisfy the output schema it was cut out of, and a truncated prefix spends the whole cap on
|
|
201
|
+
a document whose own note tells the model not to read it.
|
|
202
|
+
- **Results are compact JSON.** Two-space indentation reads better to a human than to a model and
|
|
203
|
+
costs roughly a third of every result in whitespace; a host that wants it pretty can re-print the
|
|
204
|
+
structured content it also gets.
|
|
205
|
+
- **Spending tools are called out in the server's instructions**, derived from those same
|
|
206
|
+
annotations rather than restated in prose, so a tool withheld on this server is not named as one to
|
|
207
|
+
be careful with.
|
|
122
208
|
|
|
123
209
|
## Development
|
|
124
210
|
|
|
@@ -127,8 +213,17 @@ pnpm --filter @cat-factory/mcp-server build
|
|
|
127
213
|
pnpm --filter @cat-factory/mcp-server test:run
|
|
128
214
|
pnpm gen:sdk # regenerate the tool table after a contracts change
|
|
129
215
|
pnpm check:sdk # the CI drift guard
|
|
216
|
+
pnpm check:publish # after a build: the empty-shell / publint / attw guard
|
|
130
217
|
```
|
|
131
218
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
219
|
+
Three layers of coverage, and the split is about what each CAN see:
|
|
220
|
+
|
|
221
|
+
- **Unit tests** (`test/`) drive a **real** MCP client over an in-memory transport against a real SDK
|
|
222
|
+
client whose `fetch` is stubbed, so a tool that lists but cannot be called fails a test rather than
|
|
223
|
+
shipping. `test/stdio.test.ts` covers the executable's rules (connect before announcing, every
|
|
224
|
+
human-readable byte off stdout, refuse rather than start) without spawning anything.
|
|
225
|
+
- **The MCP phase of `backend/internal/sdk-smoketest`** spawns the built `dist/bin.js` as a real
|
|
226
|
+
process against a real backend. It is the only thing that can see the published output schemas
|
|
227
|
+
DISAGREE with what the deployment actually answers, because the client validates them.
|
|
228
|
+
- **`pnpm check:publish`** covers the shape this package is most exposed to: one `bin` entry pointing
|
|
229
|
+
at a gitignored `dist`, which is exactly how two other packages once reached npm as empty shells.
|
package/dist/bin.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { ENV_VARS, optionsFromEnv } from './config.js';
|
|
4
|
-
import { createCatFactoryMcpServer } from './server.js';
|
|
2
|
+
import { bootStdioServer, startupFailureMessage } from './stdio.js';
|
|
5
3
|
// The `cat-factory-mcp` executable: the stdio server an MCP host spawns.
|
|
6
4
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
process.stderr.write(`cat-factory MCP server ready: ${tools.length} tools against ${options.baseUrl}\n`);
|
|
16
|
-
}
|
|
17
|
-
main().catch((error) => {
|
|
18
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
-
process.stderr.write(`cat-factory MCP server failed to start: ${message}\n` +
|
|
20
|
-
`Configure it with ${ENV_VARS.baseUrl} and ${ENV_VARS.apiKey}.\n`);
|
|
5
|
+
// Deliberately nothing but the process wiring: the environment, the stream every human-readable
|
|
6
|
+
// byte goes to (stderr, NEVER stdout, which carries the protocol), and the exit code. Everything it
|
|
7
|
+
// decides lives in `stdio.ts`, where a test can drive it.
|
|
8
|
+
bootStdioServer({
|
|
9
|
+
env: process.env,
|
|
10
|
+
log: (line) => process.stderr.write(line),
|
|
11
|
+
}).catch((error) => {
|
|
12
|
+
process.stderr.write(startupFailureMessage(error));
|
|
21
13
|
// Refusing to start is the point: a server that comes up without credentials would be listed by
|
|
22
|
-
// the host as connected and would then fail every call, which costs a model several turns to
|
|
23
|
-
//
|
|
14
|
+
// the host as connected and would then fail every call, which costs a model several turns to work
|
|
15
|
+
// out and reads to a user as the platform being broken.
|
|
24
16
|
process.exit(1);
|
|
25
17
|
});
|
|
26
18
|
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEnE,yEAAyE;AACzE,EAAE;AACF,gGAAgG;AAChG,oGAAoG;AACpG,0DAA0D;AAE1D,eAAe,CAAC;IACd,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;CAC1C,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAA;IAClD,gGAAgG;IAChG,kGAAkG;IAClG,wDAAwD;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/dist/config.d.ts
CHANGED
|
@@ -11,6 +11,23 @@ export interface CatFactoryMcpOptions {
|
|
|
11
11
|
* silently exposed nothing would look exactly like a working server with a quiet model.
|
|
12
12
|
*/
|
|
13
13
|
groups?: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Expose only these tools, by name (`tasks_get`, `decisions_list`, …). Absent or empty ⇒ every
|
|
16
|
+
* tool the other filters leave.
|
|
17
|
+
*
|
|
18
|
+
* Beside the group filter rather than instead of it: a group is the unit an operator thinks in
|
|
19
|
+
* ("no debug tools here"), and a tool is the unit a REFUSAL needs ("everything but the one that
|
|
20
|
+
* merges"). With only the coarse filter, withholding `notifications_act` cost the whole inbox.
|
|
21
|
+
*/
|
|
22
|
+
tools?: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* Withhold these tools by name, after every other filter.
|
|
25
|
+
*
|
|
26
|
+
* The deny half is the one that carries the weight: an allow-list has to be re-edited every time
|
|
27
|
+
* `/api/v1` grows, and a forgotten edit silently withholds a new capability, where a deny-list
|
|
28
|
+
* keeps admitting new tools and goes on refusing exactly what was named.
|
|
29
|
+
*/
|
|
30
|
+
excludeTools?: readonly string[];
|
|
14
31
|
/**
|
|
15
32
|
* Expose only the tools that change nothing (the GETs). For an agent that should be able to
|
|
16
33
|
* READ a deployment and never act on it.
|
|
@@ -39,12 +56,19 @@ export interface CatFactoryMcpOptions {
|
|
|
39
56
|
export declare const ENV_VARS: {
|
|
40
57
|
readonly baseUrl: 'CAT_FACTORY_BASE_URL';
|
|
41
58
|
readonly apiKey: 'CAT_FACTORY_API_KEY';
|
|
59
|
+
readonly apiKeyFile: 'CAT_FACTORY_API_KEY_FILE';
|
|
42
60
|
readonly groups: 'CAT_FACTORY_MCP_GROUPS';
|
|
61
|
+
readonly tools: 'CAT_FACTORY_MCP_TOOLS';
|
|
62
|
+
readonly excludeTools: 'CAT_FACTORY_MCP_EXCLUDE_TOOLS';
|
|
43
63
|
readonly readOnly: 'CAT_FACTORY_MCP_READ_ONLY';
|
|
44
64
|
readonly maxResultChars: 'CAT_FACTORY_MCP_MAX_RESULT_CHARS';
|
|
45
65
|
readonly timeoutMs: 'CAT_FACTORY_MCP_TIMEOUT_MS';
|
|
46
66
|
readonly maxRetries: 'CAT_FACTORY_MCP_MAX_RETRIES';
|
|
47
67
|
};
|
|
68
|
+
/** How `optionsFromEnv` reaches the filesystem, injected so a test needs no temp file. */
|
|
69
|
+
export interface EnvReadDeps {
|
|
70
|
+
readSecretFile?: (path: string) => string;
|
|
71
|
+
}
|
|
48
72
|
/**
|
|
49
73
|
* Read the options an MCP host can supply: environment variables, because that is what every host
|
|
50
74
|
* config format (Claude Desktop, an IDE, a CI runner) can set on a stdio server it spawns.
|
|
@@ -53,7 +77,7 @@ export declare const ENV_VARS: {
|
|
|
53
77
|
* lists 36 tools and then fails every one of them is the worst of both worlds: the host reports it
|
|
54
78
|
* as connected, and the model spends turns discovering that nothing works.
|
|
55
79
|
*/
|
|
56
|
-
export declare function optionsFromEnv(env: Record<string, string | undefined
|
|
80
|
+
export declare function optionsFromEnv(env: Record<string, string | undefined>, deps?: EnvReadDeps): CatFactoryMcpOptions;
|
|
57
81
|
/**
|
|
58
82
|
* What a server exposes, and what it withheld.
|
|
59
83
|
*
|
|
@@ -70,6 +94,10 @@ export interface ToolSelection {
|
|
|
70
94
|
filteredGroups: string[];
|
|
71
95
|
/** Whether the write tools were withheld by `readOnly`. */
|
|
72
96
|
writeToolsHidden: boolean;
|
|
97
|
+
/** Tools the operator withheld BY NAME. Empty when no deny-list was applied. */
|
|
98
|
+
deniedTools: string[];
|
|
99
|
+
/** Whether an allow-list narrowed the server to an explicitly chosen set of tools. */
|
|
100
|
+
toolsAllowListed: boolean;
|
|
73
101
|
}
|
|
74
102
|
/** The tools a set of options exposes, and what was filtered out. */
|
|
75
103
|
export declare function selectTools(tools: readonly CatFactoryTool[], options: CatFactoryMcpOptions): ToolSelection;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AASnF,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAA;IACf,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAChC;AAED,wDAAwD;AACxD,eAAO,MAAM,QAAQ;aACnB,OAAO,EAAE,sBAAsB;aAC/B,MAAM,EAAE,qBAAqB;aAC7B,UAAU,EAAE,0BAA0B;aACtC,MAAM,EAAE,wBAAwB;aAChC,KAAK,EAAE,uBAAuB;aAC9B,YAAY,EAAE,+BAA+B;aAC7C,QAAQ,EAAE,2BAA2B;aACrC,cAAc,EAAE,kCAAkC;aAClD,SAAS,EAAE,4BAA4B;aACvC,UAAU,EAAE,6BAA6B;CACjC,CAAA;AAEV,0FAA0F;AAC1F,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,GAAE,WAAgB,GACrB,oBAAoB,CAwBtB;AA8ED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,gFAAgF;IAChF,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,2DAA2D;IAC3D,gBAAgB,EAAE,OAAO,CAAA;IACzB,gFAAgF;IAChF,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,sFAAsF;IACtF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CA2Cf"}
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { CAT_FACTORY_TOOL_GROUPS } from './tools.generated.js';
|
|
2
3
|
/** The environment variables `optionsFromEnv` reads. */
|
|
3
4
|
export const ENV_VARS = {
|
|
4
5
|
baseUrl: 'CAT_FACTORY_BASE_URL',
|
|
5
6
|
apiKey: 'CAT_FACTORY_API_KEY',
|
|
7
|
+
apiKeyFile: 'CAT_FACTORY_API_KEY_FILE',
|
|
6
8
|
groups: 'CAT_FACTORY_MCP_GROUPS',
|
|
9
|
+
tools: 'CAT_FACTORY_MCP_TOOLS',
|
|
10
|
+
excludeTools: 'CAT_FACTORY_MCP_EXCLUDE_TOOLS',
|
|
7
11
|
readOnly: 'CAT_FACTORY_MCP_READ_ONLY',
|
|
8
12
|
maxResultChars: 'CAT_FACTORY_MCP_MAX_RESULT_CHARS',
|
|
9
13
|
timeoutMs: 'CAT_FACTORY_MCP_TIMEOUT_MS',
|
|
@@ -17,36 +21,86 @@ export const ENV_VARS = {
|
|
|
17
21
|
* lists 36 tools and then fails every one of them is the worst of both worlds: the host reports it
|
|
18
22
|
* as connected, and the model spends turns discovering that nothing works.
|
|
19
23
|
*/
|
|
20
|
-
export function optionsFromEnv(env) {
|
|
24
|
+
export function optionsFromEnv(env, deps = {}) {
|
|
21
25
|
const baseUrl = env[ENV_VARS.baseUrl]?.trim();
|
|
22
|
-
const apiKey = env[ENV_VARS.apiKey]?.trim();
|
|
23
26
|
if (!baseUrl)
|
|
24
27
|
throw new Error(`${ENV_VARS.baseUrl} is required (the deployment's origin).`);
|
|
25
|
-
|
|
26
|
-
throw new Error(`${ENV_VARS.apiKey} is required (a public-API key).`);
|
|
27
|
-
const groups = env[ENV_VARS.groups]
|
|
28
|
-
?.split(',')
|
|
29
|
-
.map((group) => group.trim())
|
|
30
|
-
.filter(Boolean);
|
|
28
|
+
const apiKey = readApiKey(env, deps);
|
|
31
29
|
// Read each ceiling ONCE. Every one of these is optional and must stay ABSENT rather than
|
|
32
30
|
// become `undefined`, because the server spreads them onto the SDK's own options and an
|
|
33
31
|
// explicit `undefined` is not the same as not passing the field.
|
|
32
|
+
const groups = list(env[ENV_VARS.groups]);
|
|
33
|
+
const tools = list(env[ENV_VARS.tools]);
|
|
34
|
+
const excludeTools = list(env[ENV_VARS.excludeTools]);
|
|
34
35
|
const maxResultChars = numeric(env[ENV_VARS.maxResultChars], ENV_VARS.maxResultChars);
|
|
35
36
|
const timeoutMs = numeric(env[ENV_VARS.timeoutMs], ENV_VARS.timeoutMs);
|
|
36
37
|
const maxRetries = numeric(env[ENV_VARS.maxRetries], ENV_VARS.maxRetries);
|
|
37
38
|
return {
|
|
38
39
|
baseUrl,
|
|
39
40
|
apiKey,
|
|
40
|
-
...(groups
|
|
41
|
+
...(groups ? { groups } : {}),
|
|
42
|
+
...(tools ? { tools } : {}),
|
|
43
|
+
...(excludeTools ? { excludeTools } : {}),
|
|
41
44
|
...(isTruthy(env[ENV_VARS.readOnly]) ? { readOnly: true } : {}),
|
|
42
45
|
...(maxResultChars !== undefined ? { maxResultChars } : {}),
|
|
43
46
|
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
44
47
|
...(maxRetries !== undefined ? { maxRetries } : {}),
|
|
45
48
|
};
|
|
46
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The API key, from the variable or from the FILE the variable names.
|
|
52
|
+
*
|
|
53
|
+
* The file exists because a stdio server's environment is a host's config file: on every host that
|
|
54
|
+
* matters that means a long-lived credential sitting in plaintext in a user's home directory, read
|
|
55
|
+
* by every process that can read the file and copied into every backup and every screen share of
|
|
56
|
+
* that config. A path is not a secret, so pointing at one keeps the key in a file the operator can
|
|
57
|
+
* lock down (`chmod 600`, a mounted secret, a secrets-manager sidecar's drop) and out of the config
|
|
58
|
+
* that gets shared.
|
|
59
|
+
*
|
|
60
|
+
* Declaring BOTH is refused rather than resolved by precedence. Two live sources for one credential
|
|
61
|
+
* means a rotation can land on the one that is not being read, and the deployment goes on working
|
|
62
|
+
* with the old key until it is revoked, which is exactly the moment nobody is looking.
|
|
63
|
+
*/
|
|
64
|
+
function readApiKey(env, deps) {
|
|
65
|
+
const inline = env[ENV_VARS.apiKey]?.trim();
|
|
66
|
+
const path = env[ENV_VARS.apiKeyFile]?.trim();
|
|
67
|
+
if (inline && path) {
|
|
68
|
+
throw new Error(`${ENV_VARS.apiKey} and ${ENV_VARS.apiKeyFile} are both set. Pick one: two sources for one ` +
|
|
69
|
+
'credential means a rotation can land on the half nobody reads.');
|
|
70
|
+
}
|
|
71
|
+
if (inline)
|
|
72
|
+
return inline;
|
|
73
|
+
if (!path) {
|
|
74
|
+
throw new Error(`${ENV_VARS.apiKey} is required (a public-API key), or ${ENV_VARS.apiKeyFile} naming a file ` +
|
|
75
|
+
'that holds one.');
|
|
76
|
+
}
|
|
77
|
+
const read = deps.readSecretFile ?? ((target) => readFileSync(target, 'utf8'));
|
|
78
|
+
let contents;
|
|
79
|
+
try {
|
|
80
|
+
contents = read(path);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// The PATH is named and the cause is passed through; the contents never are, on any branch of
|
|
84
|
+
// this function, because this message goes to a host's log.
|
|
85
|
+
throw new Error(`${ENV_VARS.apiKeyFile} points at ${path}, which could not be read: ` +
|
|
86
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
87
|
+
}
|
|
88
|
+
const key = contents.trim();
|
|
89
|
+
if (!key)
|
|
90
|
+
throw new Error(`${ENV_VARS.apiKeyFile} points at ${path}, which is empty.`);
|
|
91
|
+
return key;
|
|
92
|
+
}
|
|
47
93
|
function isTruthy(value) {
|
|
48
94
|
return value === '1' || value?.toLowerCase() === 'true';
|
|
49
95
|
}
|
|
96
|
+
/** A comma-separated list, or undefined when the variable is unset or lists nothing. */
|
|
97
|
+
function list(value) {
|
|
98
|
+
const entries = value
|
|
99
|
+
?.split(',')
|
|
100
|
+
.map((entry) => entry.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
return entries && entries.length > 0 ? entries : undefined;
|
|
103
|
+
}
|
|
50
104
|
/**
|
|
51
105
|
* A numeric env value, or undefined when unset.
|
|
52
106
|
*
|
|
@@ -73,12 +127,50 @@ export function selectTools(tools, options) {
|
|
|
73
127
|
throw new Error(`Unknown tool group(s): ${unknown.join(', ')}. Known groups: ${[...known].join(', ')}.`);
|
|
74
128
|
}
|
|
75
129
|
}
|
|
76
|
-
|
|
130
|
+
// Both name filters are checked against the WHOLE table rather than against what the group and
|
|
131
|
+
// read-only filters have already left, so a name is judged on whether it exists at all. Checking
|
|
132
|
+
// against the survivors would turn a redundant deny (a tool in a group that is off anyway) into a
|
|
133
|
+
// startup failure, and redundancy in a safety list is not a mistake worth refusing to boot over.
|
|
134
|
+
const allowed = namedTools(tools, options.tools, ENV_VARS.tools);
|
|
135
|
+
const denied = namedTools(tools, options.excludeTools, ENV_VARS.excludeTools);
|
|
136
|
+
const exposed = tools.filter((tool) => (!requested || requested.has(tool.group)) &&
|
|
137
|
+
(!options.readOnly || tool.readOnly) &&
|
|
138
|
+
(!allowed || allowed.has(tool.name)) &&
|
|
139
|
+
!denied?.has(tool.name));
|
|
140
|
+
// Filters that combine to expose NOTHING are refused for the same reason an unknown group name
|
|
141
|
+
// is: the host reports the server as connected either way, and a model with no tools looks
|
|
142
|
+
// exactly like a model that has decided not to use any.
|
|
143
|
+
if (exposed.length === 0) {
|
|
144
|
+
throw new Error('The configured filters expose no tools at all: ' +
|
|
145
|
+
`groups=${options.groups?.join(',') ?? '(all)'}, readOnly=${options.readOnly === true}, ` +
|
|
146
|
+
`tools=${options.tools?.join(',') ?? '(all)'}, ` +
|
|
147
|
+
`excludeTools=${options.excludeTools?.join(',') ?? '(none)'}.`);
|
|
148
|
+
}
|
|
77
149
|
const filteredGroups = [...known].filter((group) => requested !== null && !requested.has(group));
|
|
78
150
|
return {
|
|
79
151
|
exposed,
|
|
80
152
|
filteredGroups,
|
|
81
153
|
writeToolsHidden: options.readOnly === true,
|
|
154
|
+
deniedTools: denied ? [...denied].sort() : [],
|
|
155
|
+
toolsAllowListed: allowed !== null,
|
|
82
156
|
};
|
|
83
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* A validated set of tool names, or null when the filter is unset.
|
|
160
|
+
*
|
|
161
|
+
* An unknown name throws, naming the variable that carried it. A deny-list is the one filter whose
|
|
162
|
+
* typo is silently DANGEROUS: `notifications_action` withholds nothing, and the tool the operator
|
|
163
|
+
* meant to keep away from a model goes on being served.
|
|
164
|
+
*/
|
|
165
|
+
function namedTools(tools, names, variable) {
|
|
166
|
+
if (!names?.length)
|
|
167
|
+
return null;
|
|
168
|
+
const known = new Set(tools.map((tool) => tool.name));
|
|
169
|
+
const unknown = names.filter((name) => !known.has(name));
|
|
170
|
+
if (unknown.length > 0) {
|
|
171
|
+
throw new Error(`${variable} names tool(s) this server does not have: ${unknown.join(', ')}. ` +
|
|
172
|
+
`A tool is named <group>_<method>; the known ones are: ${[...known].sort().join(', ')}.`);
|
|
173
|
+
}
|
|
174
|
+
return new Set(names);
|
|
175
|
+
}
|
|
84
176
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAuB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createCatFactoryMcpServer, type CatFactoryMcpServer, MCP_SERVER_NAME, MCP_SERVER_VERSION, } from './server.ts';
|
|
2
|
-
export { type CatFactoryMcpOptions, ENV_VARS, optionsFromEnv, selectTools } from './config.ts';
|
|
2
|
+
export { type CatFactoryMcpOptions, ENV_VARS, type EnvReadDeps, optionsFromEnv, selectTools, type ToolSelection, } from './config.ts';
|
|
3
3
|
export { buildInstructions } from './instructions.ts';
|
|
4
4
|
export { DEFAULT_MAX_RESULT_CHARS, renderError, renderResult, type ToolResult } from './result.ts';
|
|
5
|
+
export { bootStdioServer, type StdioBootDeps, type StdioBootResult, startupFailureMessage, } from './stdio.ts';
|
|
5
6
|
export { CAT_FACTORY_OMITTED_OPERATIONS, CAT_FACTORY_TOOL_GROUPS, CAT_FACTORY_TOOLS, type CatFactoryOmittedOperation, type CatFactoryTool, } from './tools.generated.ts';
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,KAAK,oBAAoB,EACzB,QAAQ,EACR,KAAK,WAAW,EAChB,cAAc,EACd,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAClG,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,iBAAiB,EACjB,KAAK,0BAA0B,EAC/B,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
// Run it as a stdio server with the `cat-factory-mcp` binary, or mount it on your own transport
|
|
9
9
|
// with `createCatFactoryMcpServer`.
|
|
10
10
|
export { createCatFactoryMcpServer, MCP_SERVER_NAME, MCP_SERVER_VERSION, } from './server.js';
|
|
11
|
-
export { ENV_VARS, optionsFromEnv, selectTools } from './config.js';
|
|
11
|
+
export { ENV_VARS, optionsFromEnv, selectTools, } from './config.js';
|
|
12
12
|
export { buildInstructions } from './instructions.js';
|
|
13
13
|
export { DEFAULT_MAX_RESULT_CHARS, renderError, renderResult } from './result.js';
|
|
14
|
+
export { bootStdioServer, startupFailureMessage, } from './stdio.js';
|
|
14
15
|
export { CAT_FACTORY_OMITTED_OPERATIONS, CAT_FACTORY_TOOL_GROUPS, CAT_FACTORY_TOOLS, } from './tools.generated.js';
|
|
15
16
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,EAAE;AACF,+FAA+F;AAC/F,mGAAmG;AACnG,8FAA8F;AAC9F,uFAAuF;AACvF,EAAE;AACF,gGAAgG;AAChG,oCAAoC;AAEpC,OAAO,EACL,yBAAyB,EAEzB,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,EAAE;AACF,+FAA+F;AAC/F,mGAAmG;AACnG,8FAA8F;AAC9F,uFAAuF;AACvF,EAAE;AACF,gGAAgG;AAChG,oCAAoC;AAEpC,OAAO,EACL,yBAAyB,EAEzB,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAEL,QAAQ,EAER,cAAc,EACd,WAAW,GAEZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAA;AAClG,OAAO,EACL,eAAe,EAGf,qBAAqB,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,iBAAiB,GAGlB,MAAM,sBAAsB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAkElE"}
|