@ts-pf/mcp 0.0.0-stage → 0.0.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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -2
  3. package/dist/all.d.ts +63 -0
  4. package/dist/all.d.ts.map +1 -0
  5. package/dist/all.js +55 -0
  6. package/dist/all.js.map +1 -0
  7. package/dist/audit.d.ts +21 -0
  8. package/dist/audit.d.ts.map +1 -0
  9. package/dist/audit.js +85 -0
  10. package/dist/audit.js.map +1 -0
  11. package/dist/bind.d.ts +42 -0
  12. package/dist/bind.d.ts.map +1 -0
  13. package/dist/bind.js +46 -0
  14. package/dist/bind.js.map +1 -0
  15. package/dist/bound-tool.d.ts +18 -0
  16. package/dist/bound-tool.d.ts.map +1 -0
  17. package/dist/bound-tool.js +31 -0
  18. package/dist/bound-tool.js.map +1 -0
  19. package/dist/call.d.ts +12 -0
  20. package/dist/call.d.ts.map +1 -0
  21. package/dist/call.js +104 -0
  22. package/dist/call.js.map +1 -0
  23. package/dist/composite.d.ts +17 -0
  24. package/dist/composite.d.ts.map +1 -0
  25. package/dist/composite.js +14 -0
  26. package/dist/composite.js.map +1 -0
  27. package/dist/coverage.d.ts +11 -0
  28. package/dist/coverage.d.ts.map +1 -0
  29. package/dist/coverage.js +35 -0
  30. package/dist/coverage.js.map +1 -0
  31. package/dist/error-text.d.ts +25 -0
  32. package/dist/error-text.d.ts.map +1 -0
  33. package/dist/error-text.js +56 -0
  34. package/dist/error-text.js.map +1 -0
  35. package/dist/executor.d.ts +19 -0
  36. package/dist/executor.d.ts.map +1 -0
  37. package/dist/executor.js +49 -0
  38. package/dist/executor.js.map +1 -0
  39. package/dist/host.d.ts +13 -0
  40. package/dist/host.d.ts.map +1 -0
  41. package/dist/host.js +226 -0
  42. package/dist/host.js.map +1 -0
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +5 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/infer.d.ts +17 -0
  48. package/dist/infer.d.ts.map +1 -0
  49. package/dist/infer.js +2 -0
  50. package/dist/infer.js.map +1 -0
  51. package/dist/meta.d.ts +20 -0
  52. package/dist/meta.d.ts.map +1 -0
  53. package/dist/meta.js +10 -0
  54. package/dist/meta.js.map +1 -0
  55. package/dist/names.d.ts +22 -0
  56. package/dist/names.d.ts.map +1 -0
  57. package/dist/names.js +14 -0
  58. package/dist/names.js.map +1 -0
  59. package/dist/node.d.ts +41 -0
  60. package/dist/node.d.ts.map +1 -0
  61. package/dist/node.js +47 -0
  62. package/dist/node.js.map +1 -0
  63. package/dist/result.d.ts +7 -0
  64. package/dist/result.d.ts.map +1 -0
  65. package/dist/result.js +31 -0
  66. package/dist/result.js.map +1 -0
  67. package/dist/schema.d.ts +23 -0
  68. package/dist/schema.d.ts.map +1 -0
  69. package/dist/schema.js +172 -0
  70. package/dist/schema.js.map +1 -0
  71. package/dist/sdk.d.ts +31 -0
  72. package/dist/sdk.d.ts.map +1 -0
  73. package/dist/sdk.js +87 -0
  74. package/dist/sdk.js.map +1 -0
  75. package/dist/search.d.ts +13 -0
  76. package/dist/search.d.ts.map +1 -0
  77. package/dist/search.js +52 -0
  78. package/dist/search.js.map +1 -0
  79. package/dist/tool-meta.d.ts +14 -0
  80. package/dist/tool-meta.d.ts.map +1 -0
  81. package/dist/tool-meta.js +44 -0
  82. package/dist/tool-meta.js.map +1 -0
  83. package/dist/tool.d.ts +102 -0
  84. package/dist/tool.d.ts.map +1 -0
  85. package/dist/tool.js +30 -0
  86. package/dist/tool.js.map +1 -0
  87. package/dist/toolkit.d.ts +36 -0
  88. package/dist/toolkit.d.ts.map +1 -0
  89. package/dist/toolkit.js +22 -0
  90. package/dist/toolkit.js.map +1 -0
  91. package/dist/toolset.d.ts +89 -0
  92. package/dist/toolset.d.ts.map +1 -0
  93. package/dist/toolset.js +67 -0
  94. package/dist/toolset.js.map +1 -0
  95. package/dist/types.d.ts +57 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +2 -0
  98. package/dist/types.js.map +1 -0
  99. package/package.json +80 -4
  100. package/skills/ts-pf-mcp/SKILL.md +134 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ts-pf contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,192 @@
1
- # Temporary Holding Version
1
+ # @ts-pf/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Opt-in MCP tools from a ts-pf contract. The contract defines the tools, an executor runs them (your `app` in-process, or a typed client), and the official MCP SDK v2 serves them. A projection plus an executor: no MCP wire code here, and no ts-pf HTTP or message handler is reused.
4
+
5
+ Agent skill: [`skills/ts-pf-mcp/`](skills/ts-pf-mcp/). Sync with `npx skills experimental_sync -y`.
6
+
7
+ Three entries:
8
+
9
+ - `@ts-pf/mcp/meta`: `mcp({ effect, description, aliases })`, dependency-free, safe in contract files.
10
+ - `@ts-pf/mcp`: the toolkit (`createToolkit`, `all`, `bindTools`, `audit`). Depends on `@ts-pf/contract`, `@ts-pf/docs`, `@ts-pf/server`, `@ts-pf/protocol`.
11
+ - `@ts-pf/mcp/sdk`: `createMcpFactory`, a per-request `McpServer` factory. Peer **`@modelcontextprotocol/server` >=2.4.0 <3**.
12
+
13
+ ## One call
14
+
15
+ Every procedure becomes a tool named by its path (`planet.find` → `planet_find`); `docs({ hidden: true })` procedures are skipped. `context` runs once per call and is typed from the app's `$context<C>()`.
16
+
17
+ ```ts
18
+ import { createMcpFactory } from '@ts-pf/mcp/sdk'
19
+
20
+ export const factory = createMcpFactory(app, { name: 'planets', version: '1.0.0' })
21
+ ```
22
+
23
+ ## Shape tools, compose toolsets, check coverage
24
+
25
+ Classify in the contract with `.meta({ ...docs({ description }), ...mcp({ effect: 'read' }) })`; unclassified tools count as `write`. Descriptions come from `.describe()`, then `mcp({ description })`, then `docs()` description / summary.
26
+
27
+ ```ts
28
+ import { all, bindTools, createToolkit } from '@ts-pf/mcp'
29
+ import { createMcpFactory } from '@ts-pf/mcp/sdk'
30
+ import { contract } from './contract.js'
31
+
32
+ const tk = createToolkit(contract)
33
+
34
+ const findPlanet = tk.planet.find
35
+ .describe('Get one planet by id. Call planet_list first if you only have a name.')
36
+ .errors({ NOT_FOUND: ({ data }) => `No planet ${data.id}. Call planet_list for ids.` })
37
+
38
+ export const planetsMcp = tk.$toolset({
39
+ tools: [all(tk.planet, { exclude: [tk.planet.find] }), { planet_find: findPlanet }],
40
+ })
41
+
42
+ // A new procedure fails this line until a toolset exposes it or `exclude` names it.
43
+ tk.$coverage({ toolsets: [planetsMcp] })
44
+
45
+ export const tools = bindTools(planetsMcp, { app, context: () => ({ db }) })
46
+ export const factory = createMcpFactory(tools, { name: 'planets', version: '1.0.0' })
47
+ ```
48
+
49
+ Builder methods: `.describe(text)`, `.effect('read' | 'write' | 'destructive')`, `.inject({ key: fn })` (hidden from the model, filled per call), `.input(schema, map)` (a new model input; exclusive with `.inject`), `.output(map)` / `.output(schema, map)` (mappers get `{ context, info, call }`, `call` a typed client for joins; `outputSchema` and `structuredContent` only with a schema, and results are then checked), `.errors({ CODE: fn })`, `.examples([...])`. `tk.$tool({ description, effect, input, run })` is a composite tool over `call`.
50
+
51
+ `all(node, { effect, tags, exclude, inject })` makes default tools for a node; each `inject` key goes only to tools whose input has it, and a typo or a type conflict is a compile error naming the field.
52
+
53
+ Large groups go behind a host: `tk.$host({ description, tools, split })` lists `get_<key>_query_capabilities` / `call_<key>_query_capability` (read) and `get_<key>_operations_capabilities` / `call_<key>_operations_capability` (write + destructive), or `get_<key>_capabilities` / `call_<key>_capability` with `split: false`. Discovery takes `{ query, category, names, detail, limit }` and ranks with BM25. Flat tools do best up to about 15 per toolset; `audit()` warns above that.
54
+
55
+ ## Serve
56
+
57
+ `@modelcontextprotocol/server` owns the transport. Over HTTP, pass the verified token as `authInfo`; it reaches `context` as `info.authInfo`. Request headers are not forwarded to procedures; `context` sees the `Request` as `info.requestInfo` and should derive identity from `authInfo`, not raw `Authorization` / `Cookie`. stdio never carries a token, so give identity from the process: `serveStdio((ctx) => factory({ ...ctx, authInfo }))` with `serveStdio` from `@modelcontextprotocol/server/stdio`.
58
+
59
+ ```ts
60
+ import { createMcpHandler } from '@modelcontextprotocol/server'
61
+ import { createMcpFactory } from '@ts-pf/mcp/sdk'
62
+
63
+ const factory = createMcpFactory(app, { name: 'planets', version: '1.0.0' })
64
+ const mcpHandler = createMcpHandler(factory)
65
+ export const response = await mcpHandler.fetch(request)
66
+ ```
67
+
68
+ ## OAuth
69
+
70
+ An HTTP MCP server is an OAuth resource server. The SDK checks the token at the HTTP edge, before any MCP message is handled: a client starts sign-in only on a `401` with a `WWW-Authenticate` challenge, and an error from a tool call does not trigger it. ts-pf takes over from the verified `AuthInfo`. `context` maps your IdP's claims into the app's context, then the procedure's own middleware runs as usual.
71
+
72
+ ```ts
73
+ import {
74
+ createMcpHandler,
75
+ getOAuthProtectedResourceMetadataUrl,
76
+ OAuthError,
77
+ OAuthErrorCode,
78
+ type OAuthMetadata,
79
+ type OAuthTokenVerifier,
80
+ oauthMetadataResponse,
81
+ requireBearerAuth,
82
+ } from '@modelcontextprotocol/server'
83
+ import { createMcpFactory } from '@ts-pf/mcp/sdk'
84
+ import { PFError } from '@ts-pf/protocol'
85
+
86
+ const issuer = 'https://idp.example.com'
87
+ const resourceServerUrl = new URL('https://api.example.com/mcp')
88
+
89
+ // Your IdP's RFC 8414 metadata, re-served for clients that probe this origin.
90
+ // Fetched once at startup (fails fast if the IdP is down); hardcode it if you prefer.
91
+ const oauthMetadata = (await (
92
+ await fetch(`${issuer}/.well-known/oauth-authorization-server`)
93
+ ).json()) as OAuthMetadata
94
+
95
+ const invalid = (message: string) =>
96
+ new OAuthError(OAuthErrorCode.InvalidToken, message)
97
+
98
+ // Introspection (RFC 7662) as this server's own client; validating a JWT against the IdP's JWKS works too.
99
+ // Anything unexpected is `invalid_token` (a 401 challenge), never a crash.
100
+ const verifier: OAuthTokenVerifier = {
101
+ async verifyAccessToken(token) {
102
+ const response = await fetch(`${issuer}/oauth/introspect`, {
103
+ method: 'POST',
104
+ headers: {
105
+ authorization: `Basic ${btoa(`${env.MCP_CLIENT_ID}:${env.MCP_CLIENT_SECRET}`)}`,
106
+ },
107
+ body: new URLSearchParams({ token }),
108
+ })
109
+ if (!response.ok) throw invalid('Introspection failed')
110
+ const claims = (await response.json()) as {
111
+ active?: boolean
112
+ sub?: string
113
+ client_id?: string
114
+ scope?: string
115
+ exp?: number
116
+ aud?: string | string[]
117
+ }
118
+ if (!claims.active || typeof claims.sub !== 'string') {
119
+ throw invalid('Token is not active')
120
+ }
121
+ if (typeof claims.exp !== 'number') throw invalid('Token has no expiry')
122
+ if (![claims.aud ?? []].flat().includes(resourceServerUrl.href)) {
123
+ throw invalid('Token was issued for another resource')
124
+ }
125
+ return {
126
+ token,
127
+ clientId: claims.client_id ?? '',
128
+ scopes: claims.scope?.split(' ') ?? [],
129
+ expiresAt: claims.exp, // required: the SDK refuses tokens without it
130
+ resource: resourceServerUrl,
131
+ extra: { sub: claims.sub },
132
+ }
133
+ },
134
+ }
135
+
136
+ const gate = requireBearerAuth({
137
+ verifier,
138
+ requiredScopes: ['mcp'],
139
+ expectedResource: resourceServerUrl, // refuse tokens issued for another audience
140
+ resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(resourceServerUrl),
141
+ })
142
+
143
+ // IdP claims → the contract's context. Procedure middleware then sees AppCtx, as on any other transport.
144
+ const factory = createMcpFactory(app, {
145
+ name: 'planets',
146
+ version: '1.0.0',
147
+ context: async (info): Promise<AppCtx> => {
148
+ const sub = info.authInfo?.extra?.sub
149
+ if (typeof sub !== 'string') {
150
+ // `extra` is whatever the verifier set; stdio and tests have no token at all.
151
+ throw new PFError({ code: 'UNAUTHORIZED', message: 'Token has no subject' })
152
+ }
153
+ return { db, user: { id: sub } } // or look the user up by IdP subject
154
+ },
155
+ })
156
+ const mcpHandler = createMcpHandler(factory)
157
+
158
+ export async function fetchHandler(request: Request): Promise<Response> {
159
+ // `/.well-known/oauth-protected-resource/mcp` (RFC 9728) and the AS metadata.
160
+ const metadata = oauthMetadataResponse(request, { oauthMetadata, resourceServerUrl })
161
+ if (metadata) return metadata
162
+ const authInfo = await gate(request)
163
+ if (authInfo instanceof Response) return authInfo // 401 / 403 with the challenge
164
+ return mcpHandler.fetch(request, { authInfo })
165
+ }
166
+ ```
167
+
168
+ - With a toolkit, the same function goes on the binding: `bindTools(toolset, { app, context: (info) => … })`. `.inject()` and output mappers get both the mapped `context` and the raw `info.authInfo`.
169
+ - A gateway that calls an upstream ts-pf API as the user builds a client per caller: `bindTools(toolset, { client: (info) => createClient(…) })`, with the token from `info.authInfo` (exchanged for an upstream one when your IdP requires it).
170
+ - `context` can reject per tool (`info.tool`), for example when a scope is missing. The model gets the error as text; v1 does not answer it with a `403 insufficient_scope` step-up.
171
+ - Every caller gets the same `tools/list`. Enforce roles and scopes in procedure middleware, not by hiding tools.
172
+
173
+ `tools.list(info)` and `tools.call(name, args, info)` work without MCP, in tests or an agent framework. `McpCallInfo` is `{ authInfo?, requestInfo?, signal?, inputResponses?, era?, tool? }`; `tool` (`{ name, path }`) is set before `context` runs, so a project that wants tool-level tracing in interceptors copies it into its own context type.
174
+
175
+ Call pipeline options live on the executor: `bindTools(toolset, { app, context, interceptors?, onError?, maxResultChars? })` or `bindTools(toolset, { client, context?, onError?, maxResultChars? })` (`client` may be `(info) => client`; `context` is required when the toolkit's `$context<C>()` type has required keys). `createMcpFactory(app, …)` takes the same options plus `name`, `version`, `instructions`, `listTtlMs`. Unknown throws become `INTERNAL: Internal server error` and are logged with `console.error` unless `onError` is set, like `FetchHandler`. Text results are cut at `maxResultChars` (default 25,000).
176
+
177
+ `audit(toolsets, { maxTools?, maxNameLength?, missingDescription? })` returns `AuditFinding[]` (`{ level, tool, message }`) for CI: missing descriptions (`missingDescription`, default `'error'`), unclassified tools, schema roots, more than `maxTools` (15) listed tools, and names over `maxNameLength` (60) characters or outside the MCP name charset.
178
+
179
+ ## API
180
+
181
+ - `mcp`
182
+ - `createToolkit`
183
+ - `all`
184
+ - `bindTools`
185
+ - `audit`
186
+ - `createMcpFactory`
187
+ - `McpTools`
188
+ - `McpCallInfo`
189
+ - `McpToolDefinition`
190
+ - `McpToolResult`
191
+
192
+ `mcp` is `@ts-pf/mcp/meta`. `createMcpFactory` is `@ts-pf/mcp/sdk`. The rest are `@ts-pf/mcp`, which also exports the types that appear in inferred toolkit values (`Toolkit`, `ToolkitNode`, `ToolBuilder`, `Toolset`, `Host`, `AllTools`, `CompositeTool`, …) so a toolset can be exported under `declaration: true`.
package/dist/all.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ import type { DistOmit, FieldOf, KeysOfUnion, Meta } from './infer.js';
2
+ import type { Effect } from './meta.js';
3
+ import { type ToolName } from './names.js';
4
+ import { type AnyNode, type NodeBrand, type NodeCtx } from './node.js';
5
+ import { type AnyTool, type ToolBuilder } from './tool.js';
6
+ import type { McpCallInfo } from './types.js';
7
+ type LeafTools<N> = {
8
+ [K in Exclude<keyof N, NodeBrand>]: N[K] extends AnyTool ? N[K] : LeafTools<N[K]>;
9
+ }[Exclude<keyof N, NodeBrand>];
10
+ /** A tool excludes its own path; a node excludes everything under its prefix. */
11
+ export type ExcludedPath<X> = X extends AnyTool ? X['~tool']['path'] : X extends {
12
+ readonly [K in NodeBrand]?: {
13
+ prefix: infer P extends string;
14
+ };
15
+ } ? `${P}${string}` : never;
16
+ /** Effect from typed meta `{ mcp: { effect } }`; unclassified counts as `write`. */
17
+ type ToolEffect<Tl> = Tl extends ToolBuilder<infer _T, infer P, infer _C, infer _Path, infer _S> ? Meta<P> extends {
18
+ mcp: {
19
+ effect: infer E extends Effect;
20
+ };
21
+ } ? E : 'write' : 'write';
22
+ type Injected<Tl, IK extends PropertyKey> = [IK] extends [never] ? Tl : Tl extends ToolBuilder<infer T, infer P, infer C, infer Path, infer S> ? [IK & KeysOfUnion<S['model']>] extends [never] ? Tl : ToolBuilder<T, P, C, Path, {
23
+ model: DistOmit<S['model'], IK>;
24
+ mode: 'inject';
25
+ }> : Tl;
26
+ /**
27
+ * Filters once into `Selected` (shared with the inject check), then maps. A
28
+ * filtering `as` clause here would re-run over every leaf on each `keyof`,
29
+ * which the toolset name checks take several times.
30
+ */
31
+ export type AllTools<N, X, E, IK extends PropertyKey> = {
32
+ [Tl in Selected<N, X, E> as ToolName<Tl['~tool']['path']>]: Injected<Tl, IK>;
33
+ };
34
+ type ModelOf<Tl> = Tl extends AnyTool ? Tl['~tool']['model'] : never;
35
+ /** Concrete (`N` is fixed before `inject` is checked), so it contextually types `context`. */
36
+ type InjectCtx<N> = {
37
+ [k: string]: (o: {
38
+ context: NodeCtx<N>;
39
+ info: McpCallInfo;
40
+ }) => unknown;
41
+ };
42
+ type Selected<N, X, E> = LeafTools<N> extends infer Tl ? Tl extends AnyTool ? Tl['~tool']['path'] extends ExcludedPath<X> ? never : ToolEffect<Tl> extends E ? Tl : never : never : never;
43
+ type ConflictPaths<Tl, K extends PropertyKey, V> = Tl extends AnyTool ? K extends KeysOfUnion<Tl['~tool']['model']> ? [V] extends [FieldOf<Tl['~tool']['model'], K>] ? never : `${Tl['~tool']['path']}.${K & string}` : never : never;
44
+ /** Per key: not an input field anywhere → message; value does not fit some field → message naming it. */
45
+ type AllInjectCheck<Tls, Inj> = {
46
+ [K in keyof Inj]: K extends KeysOfUnion<ModelOf<Tls>> ? ConflictPaths<Tls, K, Inj[K] extends (...a: never[]) => infer R ? Awaited<R> : never> extends infer C extends string ? [C] extends [never] ? unknown : `Injected "${K & string}" does not fit field ${C}` : unknown : `"${K & string}" is not an input field of any selected procedure`;
47
+ };
48
+ export type AllOptions<N, X extends readonly unknown[], E, Inj> = {
49
+ exclude?: X;
50
+ effect?: E | readonly E[];
51
+ /** `docs()` tags; a procedure matches when it has any of them. Filtered at runtime only. */
52
+ tags?: readonly string[];
53
+ /** Each key goes only to tools whose input has it. */
54
+ inject?: Inj & InjectCtx<N> & AllInjectCheck<Selected<N, X[number], E>, Inj>;
55
+ };
56
+ /**
57
+ * Default tools for every procedure under `node`, named by path. Skips
58
+ * `docs({ hidden: true })` procedures, then filters by `exclude`, `effect`,
59
+ * and `tags`, and applies `inject` per tool.
60
+ */
61
+ export declare function all<N extends AnyNode, const X extends readonly (AnyTool | AnyNode)[] = [], const E extends Effect = Effect, Inj extends object = {}>(node: N, options?: AllOptions<N, X, E, Inj>): AllTools<N, X[number], E, keyof Inj>;
62
+ export {};
63
+ //# sourceMappingURL=all.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"all.d.ts","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"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"}
package/dist/all.js ADDED
@@ -0,0 +1,55 @@
1
+ import { walkContract } from '@ts-pf/docs';
2
+ import { toolName } from './names.js';
3
+ import { nodeInfo } from './node.js';
4
+ import { procedureInputKeys } from './schema.js';
5
+ import { createToolBuilder, isTool, procedureSpec, toolSpec, } from './tool.js';
6
+ import { effectOf, isHidden, tagsOf } from './tool-meta.js';
7
+ function exclusion(value) {
8
+ if (isTool(value))
9
+ return { path: toolSpec(value).path, subtree: false };
10
+ const info = nodeInfo(value);
11
+ if (info)
12
+ return { path: info.path.join('.'), subtree: true };
13
+ throw new TypeError('all() exclude takes toolkit tools or nodes');
14
+ }
15
+ function isExcluded(path, exclusions) {
16
+ return exclusions.some((x) => path === x.path ||
17
+ (x.subtree && (x.path === '' || path.startsWith(`${x.path}.`))));
18
+ }
19
+ /**
20
+ * Default tools for every procedure under `node`, named by path. Skips
21
+ * `docs({ hidden: true })` procedures, then filters by `exclude`, `effect`,
22
+ * and `tags`, and applies `inject` per tool.
23
+ */
24
+ export function all(node, options) {
25
+ const info = nodeInfo(node);
26
+ if (!info) {
27
+ throw new TypeError('all() takes a toolkit node, e.g. all(tk.user)');
28
+ }
29
+ const exclusions = (options?.exclude ?? []).map(exclusion);
30
+ const effects = options?.effect === undefined ? undefined : [options.effect].flat();
31
+ const tags = options?.tags;
32
+ const inject = (options?.inject ?? {});
33
+ const tools = {};
34
+ for (const entry of walkContract(info.contract)) {
35
+ const path = [...info.path, ...entry.path].join('.');
36
+ const spec = procedureSpec(path, entry.procedure);
37
+ if (isHidden(spec) || isExcluded(path, exclusions))
38
+ continue;
39
+ if (effects && !effects.includes(effectOf(spec)))
40
+ continue;
41
+ if (tags && !tagsOf(spec).some((tag) => tags.includes(tag)))
42
+ continue;
43
+ const keys = procedureInputKeys(spec).filter((key) => Object.hasOwn(inject, key));
44
+ const name = toolName(path);
45
+ if (Object.hasOwn(tools, name)) {
46
+ throw new Error(`all(): duplicate tool name "${name}"`);
47
+ }
48
+ tools[name] = createToolBuilder({
49
+ ...spec,
50
+ inject: Object.fromEntries(keys.map((key) => [key, inject[key]])),
51
+ });
52
+ }
53
+ return tools;
54
+ }
55
+ //# sourceMappingURL=all.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"all.js","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type AnyToolset } from './toolset.js';
2
+ export type AuditFinding = {
3
+ level: 'error' | 'warn';
4
+ /** A tool name, or `toolset[i]` for a finding about a whole toolset. */
5
+ tool: string;
6
+ message: string;
7
+ };
8
+ export type AuditOptions = {
9
+ /** Warn when a toolset lists more MCP tools than this. Default 15. */
10
+ maxTools?: number;
11
+ /** Warn above this name length (a soft limit for Cursor; the spec allows 128). Default 60. */
12
+ maxNameLength?: number;
13
+ /** Severity of an empty description. Default `'error'` (descriptions come from `docs()` first). */
14
+ missingDescription?: 'error' | 'warn';
15
+ };
16
+ /**
17
+ * Runtime lint for CI: descriptions, classification, schema roots, toolset
18
+ * size, and names. Coverage is `$coverage`'s job, not this one's.
19
+ */
20
+ export declare function audit(toolsets: readonly AnyToolset[], options?: AuditOptions): AuditFinding[];
21
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mGAAmG;IACnG,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACtC,CAAA;AAeD;;;GAGG;AACH,wBAAgB,KAAK,CACnB,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,OAAO,GAAE,YAAiB,GACzB,YAAY,EAAE,CAsEhB"}
package/dist/audit.js ADDED
@@ -0,0 +1,85 @@
1
+ import { bindTool } from './bound-tool.js';
2
+ import { declaredEffect, effectOf } from './tool-meta.js';
3
+ import { hostToolNames, toolsetSpec, } from './toolset.js';
4
+ const MCP_NAME = /^[A-Za-z0-9_.-]{1,128}$/;
5
+ /** Host tool names actually listed: buckets with no tools list nothing. */
6
+ function listedHostNames(key, host) {
7
+ if (host.split !== 'effect')
8
+ return hostToolNames(key, false);
9
+ const effects = Object.values(host.tools).map(effectOf);
10
+ const [getQuery, callQuery, getOps, callOps] = hostToolNames(key, 'effect');
11
+ return [
12
+ ...(effects.includes('read') ? [getQuery, callQuery] : []),
13
+ ...(effects.some((e) => e !== 'read') ? [getOps, callOps] : []),
14
+ ].filter((name) => name !== undefined);
15
+ }
16
+ /**
17
+ * Runtime lint for CI: descriptions, classification, schema roots, toolset
18
+ * size, and names. Coverage is `$coverage`'s job, not this one's.
19
+ */
20
+ export function audit(toolsets, options = {}) {
21
+ const maxTools = options.maxTools ?? 15;
22
+ const maxNameLength = options.maxNameLength ?? 60;
23
+ const missingDescription = options.missingDescription ?? 'error';
24
+ const findings = [];
25
+ toolsets.forEach((toolset, index) => {
26
+ const spec = toolsetSpec(toolset);
27
+ const listed = [
28
+ ...Object.keys(spec.tools),
29
+ ...Object.entries(spec.hosts).flatMap(([key, host]) => listedHostNames(key, host)),
30
+ ];
31
+ if (listed.length > maxTools) {
32
+ findings.push({
33
+ level: 'warn',
34
+ tool: `toolset[${index}]`,
35
+ message: `lists ${listed.length} tools (more than ${maxTools}); split it or put a large group behind a $host`,
36
+ });
37
+ }
38
+ for (const name of listed) {
39
+ if (!MCP_NAME.test(name)) {
40
+ findings.push({
41
+ level: 'error',
42
+ tool: name,
43
+ message: 'name must be 1-128 characters of A-Z, a-z, 0-9, "_", "-", "."',
44
+ });
45
+ }
46
+ else if (name.length > maxNameLength) {
47
+ findings.push({
48
+ level: 'warn',
49
+ tool: name,
50
+ message: `name is ${name.length} characters (more than ${maxNameLength}); some clients cut or reject it`,
51
+ });
52
+ }
53
+ }
54
+ const tools = [
55
+ ...Object.entries(spec.tools),
56
+ ...Object.values(spec.hosts).flatMap((host) => Object.entries(host.tools)),
57
+ ];
58
+ for (const [name, toolSpec] of tools) {
59
+ const bound = bindTool(name, toolSpec);
60
+ if (bound.definition.description.trim() === '') {
61
+ findings.push({
62
+ level: missingDescription,
63
+ tool: name,
64
+ message: 'no description; add docs({ description }) in the contract, mcp({ description }), or .describe()',
65
+ });
66
+ }
67
+ if (toolSpec.procedure && declaredEffect(toolSpec) === undefined) {
68
+ findings.push({
69
+ level: 'warn',
70
+ tool: name,
71
+ message: 'unclassified; treated as write. Add mcp({ effect }) in the contract or .effect()',
72
+ });
73
+ }
74
+ for (const issue of bound.issues) {
75
+ findings.push({
76
+ level: issue.level,
77
+ tool: name,
78
+ message: issue.message,
79
+ });
80
+ }
81
+ }
82
+ });
83
+ return findings;
84
+ }
85
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAGL,aAAa,EACb,WAAW,GACZ,MAAM,cAAc,CAAA;AAkBrB,MAAM,QAAQ,GAAG,yBAAyB,CAAA;AAE1C,2EAA2E;AAC3E,SAAS,eAAe,CAAC,GAAW,EAAE,IAAc;IAClD,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvD,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC3E,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAChE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CACnB,QAA+B,EAC/B,UAAwB,EAAE;IAE1B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAA;IACvC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAA;IACjD,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,OAAO,CAAA;IAChE,MAAM,QAAQ,GAAmB,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG;YACb,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YAC1B,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CACpD,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAC3B;SACF,CAAA;QACD,IAAI,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,WAAW,KAAK,GAAG;gBACzB,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,qBAAqB,QAAQ,iDAAiD;aAC9G,CAAC,CAAA;QACJ,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EACL,+DAA+D;iBAClE,CAAC,CAAA;YACJ,CAAC;iBAAM,IAAI,IAAI,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,WAAW,IAAI,CAAC,MAAM,0BAA0B,aAAa,kCAAkC;iBACzG,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG;YACZ,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAC7B,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAC3B;SACF,CAAA;QACD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;YACtC,IAAI,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC/C,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,kBAAkB;oBACzB,IAAI,EAAE,IAAI;oBACV,OAAO,EACL,iGAAiG;iBACpG,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,QAAQ,CAAC,SAAS,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;gBACjE,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE,IAAI;oBACV,OAAO,EACL,kFAAkF;iBACrF,CAAC,CAAA;YACJ,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,QAAQ,CAAA;AACjB,CAAC"}
package/dist/bind.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import type { ContractClient } from '@ts-pf/contract';
2
+ import type { CallInterceptor, ImplementedRouter } from '@ts-pf/server';
3
+ import { type CallOptions } from './call.js';
4
+ import type { McpOnError } from './error-text.js';
5
+ import type { MaybePromise } from './infer.js';
6
+ import { type Toolset, type ToolsetSpec } from './toolset.js';
7
+ import type { McpCallInfo, McpTools } from './types.js';
8
+ /** Options that act inside `McpTools.call`, for both executors. */
9
+ export type CallPipelineOptions = {
10
+ /** Replaces the default `console.error` of unknown / `INTERNAL` throws. */
11
+ onError?: McpOnError;
12
+ /** Text results are cut here with a truncation note. Default 25,000. */
13
+ maxResultChars?: number;
14
+ };
15
+ export type AppBinding<T, Ctx> = CallPipelineOptions & {
16
+ app: ImplementedRouter<T, Ctx>;
17
+ /** Runs once per call, with `info.tool` set. */
18
+ context: (info: McpCallInfo) => MaybePromise<Ctx>;
19
+ /** Server `CallInterceptor`s, passed straight to `runProcedure`. */
20
+ interceptors?: readonly CallInterceptor[];
21
+ };
22
+ /**
23
+ * Mappers and `inject` still see `context`, so it is required when the
24
+ * toolset's context type has required keys (`$context<AppCtx>()`), and `{}`
25
+ * when omitted otherwise.
26
+ */
27
+ type ClientContext<Ctx> = {} extends Ctx ? {
28
+ context?: (info: McpCallInfo) => MaybePromise<Ctx>;
29
+ } : {
30
+ context: (info: McpCallInfo) => MaybePromise<Ctx>;
31
+ };
32
+ export type ClientBinding<T, Ctx> = CallPipelineOptions & {
33
+ /** A typed client, or one built per caller (a gateway calling upstream as the user). */
34
+ client: ContractClient<T> | ((info: McpCallInfo) => ContractClient<T>);
35
+ } & ClientContext<Ctx>;
36
+ /** Bound tools from runtime specs; definitions, schemas, and search indexes are built once here. */
37
+ export declare function bindToolset(spec: ToolsetSpec, options: CallOptions): McpTools;
38
+ /** Bind a toolset to an executor. The contract and context are checked against the toolset's. */
39
+ export declare function bindTools<T, Ctx>(toolset: Toolset<T, Ctx, string>, binding: AppBinding<NoInfer<T>, NoInfer<Ctx>>): McpTools;
40
+ export declare function bindTools<T, Ctx>(toolset: Toolset<T, Ctx, string>, binding: ClientBinding<NoInfer<T>, NoInfer<Ctx>>): McpTools;
41
+ export {};
42
+ //# sourceMappingURL=bind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind.d.ts","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEvE,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,WAAW,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAGjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,cAAc,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEvD,mEAAmE;AACnE,MAAM,MAAM,mBAAmB,GAAG;IAChC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,UAAU,CAAA;IACpB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,GAAG,IAAI,mBAAmB,GAAG;IACrD,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC9B,gDAAgD;IAChD,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,YAAY,CAAC,GAAG,CAAC,CAAA;IACjD,oEAAoE;IACpE,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;CAC1C,CAAA;AAED;;;;GAIG;AACH,KAAK,aAAa,CAAC,GAAG,IAAI,EAAE,SAAS,GAAG,GACpC;IAAE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,YAAY,CAAC,GAAG,CAAC,CAAA;CAAE,GACtD;IAAE,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,YAAY,CAAC,GAAG,CAAC,CAAA;CAAE,CAAA;AAEzD,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,GAAG,IAAI,mBAAmB,GAAG;IACxD,wFAAwF;IACxF,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;CACvE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;AAEtB,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,QAAQ,CAoC7E;AAED,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAC9B,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAC5C,QAAQ,CAAA;AACX,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAC9B,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAC/C,QAAQ,CAAA"}
package/dist/bind.js ADDED
@@ -0,0 +1,46 @@
1
+ import { bindTool } from './bound-tool.js';
2
+ import { callTool } from './call.js';
3
+ import { appExecutor, clientExecutor } from './executor.js';
4
+ import { hostEntries } from './host.js';
5
+ import { DEFAULT_MAX_RESULT_CHARS, errorResult } from './result.js';
6
+ import { toolsetSpec } from './toolset.js';
7
+ /** Bound tools from runtime specs; definitions, schemas, and search indexes are built once here. */
8
+ export function bindToolset(spec, options) {
9
+ const entries = new Map();
10
+ const invoke = (tool, args, info) => callTool(tool, args, info, options);
11
+ for (const [name, toolSpec] of Object.entries(spec.tools)) {
12
+ const tool = bindTool(name, toolSpec);
13
+ entries.set(name, {
14
+ definition: tool.definition,
15
+ invoke: (args, info) => invoke(tool, args, info),
16
+ });
17
+ }
18
+ for (const [key, host] of Object.entries(spec.hosts)) {
19
+ const inner = Object.entries(host.tools).map(([name, s]) => bindTool(name, s));
20
+ for (const entry of hostEntries(key, host, inner, invoke, options.maxResultChars)) {
21
+ entries.set(entry.definition.name, entry);
22
+ }
23
+ }
24
+ const definitions = [...entries.values()].map((entry) => entry.definition);
25
+ return {
26
+ list: async () => [...definitions],
27
+ async call(name, args, info = {}) {
28
+ const entry = entries.get(name);
29
+ if (!entry) {
30
+ return errorResult(`Unknown tool "${name}"`, options.maxResultChars);
31
+ }
32
+ return entry.invoke(args, info);
33
+ },
34
+ };
35
+ }
36
+ export function bindTools(toolset, binding) {
37
+ const executor = 'app' in binding
38
+ ? appExecutor(binding.app, binding.context, binding.interceptors)
39
+ : clientExecutor(binding.client, binding.context);
40
+ return bindToolset(toolsetSpec(toolset), {
41
+ executor,
42
+ onError: binding.onError,
43
+ maxResultChars: binding.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS,
44
+ });
45
+ }
46
+ //# sourceMappingURL=bind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAoB,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEtD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAA;AAEvD,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EAAkC,WAAW,EAAE,MAAM,cAAc,CAAA;AAiC1E,oGAAoG;AACpG,MAAM,UAAU,WAAW,CAAC,IAAiB,EAAE,OAAoB;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC5C,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAa,EAAE,IAAiB,EAAE,EAAE,CACnE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACrC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;YAChB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;SACjD,CAAC,CAAA;IACJ,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CACzD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAClB,CAAA;QACD,KAAK,MAAM,KAAK,IAAI,WAAW,CAC7B,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,EACN,OAAO,CAAC,cAAc,CACvB,EAAE,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAC1E,OAAO;QACL,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE;YAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,WAAW,CAAC,iBAAiB,IAAI,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;YACtE,CAAC;YACD,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,CAAC;KACF,CAAA;AACH,CAAC;AAWD,MAAM,UAAU,SAAS,CACvB,OAA0C,EAC1C,OAAuE;IAEvE,MAAM,QAAQ,GACZ,KAAK,IAAI,OAAO;QACd,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC;QACjE,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IACrD,OAAO,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE;QACvC,QAAQ;QACR,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,wBAAwB;KACnE,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { JsonSchema } from '@ts-pf/docs';
2
+ import { type SchemaIssue } from './schema.js';
3
+ import type { ToolSpec } from './tool.js';
4
+ import type { McpToolDefinition } from './types.js';
5
+ /** One tool after binding: computed once, so listing and calling only read it. */
6
+ export type BoundTool = {
7
+ readonly name: string;
8
+ readonly spec: ToolSpec;
9
+ /** The schema the model fills (injected keys removed). */
10
+ readonly inputSchema: JsonSchema;
11
+ /** `structuredContent` is returned only when `outputSchema` is advertised. */
12
+ readonly structured: boolean;
13
+ readonly definition: McpToolDefinition;
14
+ /** Schema problems for `audit()`. */
15
+ readonly issues: readonly SchemaIssue[];
16
+ };
17
+ export declare function bindTool(name: string, spec: ToolSpec): BoundTool;
18
+ //# sourceMappingURL=bound-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bound-tool.d.ts","sourceRoot":"","sources":["../src/bound-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAA;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAA;IACtC,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;CACxC,CAAA;AAQD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CAoBhE"}
@@ -0,0 +1,31 @@
1
+ import { modelInputSchema, modelOutputSchema, } from './schema.js';
2
+ import { annotationsFor, descriptionOf, effectOf } from './tool-meta.js';
3
+ function withExamples(description, examples) {
4
+ if (examples.length === 0)
5
+ return description;
6
+ const lines = examples.map((example) => JSON.stringify(example)).join('\n');
7
+ return `${description}\n\nExamples:\n${lines}`;
8
+ }
9
+ export function bindTool(name, spec) {
10
+ const input = modelInputSchema(spec);
11
+ const output = modelOutputSchema(spec);
12
+ const effect = effectOf(spec);
13
+ const definition = {
14
+ name,
15
+ description: withExamples(descriptionOf(spec), spec.examples),
16
+ inputSchema: input.schema,
17
+ annotations: annotationsFor(effect),
18
+ effect,
19
+ };
20
+ if (output.schema)
21
+ definition.outputSchema = output.schema;
22
+ return {
23
+ name,
24
+ spec,
25
+ inputSchema: input.schema,
26
+ structured: output.schema !== undefined,
27
+ definition,
28
+ issues: [...input.issues, ...output.issues],
29
+ };
30
+ }
31
+ //# sourceMappingURL=bound-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bound-tool.js","sourceRoot":"","sources":["../src/bound-tool.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAgBxE,SAAS,YAAY,CAAC,WAAmB,EAAE,QAA4B;IACrE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAA;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,OAAO,GAAG,WAAW,kBAAkB,KAAK,EAAE,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAc;IACnD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACpC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC7B,MAAM,UAAU,GAAsB;QACpC,IAAI;QACJ,WAAW,EAAE,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC;QAC7D,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC;QACnC,MAAM;KACP,CAAA;IACD,IAAI,MAAM,CAAC,MAAM;QAAE,UAAU,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,CAAA;IAC1D,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,UAAU,EAAE,MAAM,CAAC,MAAM,KAAK,SAAS;QACvC,UAAU;QACV,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC;KAC5C,CAAA;AACH,CAAC"}