@webpieces/core-util 0.4.934 → 0.4.935
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 +4 -2
- package/package.json +1 -1
- package/src/http/api-type.js +2 -2
- package/src/http/api-type.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +5 -3
- package/src/index.js.map +1 -1
- package/src/mcp/McpCompileAssertions.js +19 -9
- package/src/mcp/McpCompileAssertions.js.map +1 -1
- package/src/mcp/McpMetadata.d.ts +22 -22
- package/src/mcp/McpMetadata.js +29 -30
- package/src/mcp/McpMetadata.js.map +1 -1
- package/src/mcp/McpToolCatalogFile.d.ts +16 -8
- package/src/mcp/McpToolCatalogFile.js +22 -7
- package/src/mcp/McpToolCatalogFile.js.map +1 -1
- package/src/mcp/McpToolName.d.ts +22 -0
- package/src/mcp/McpToolName.js +45 -0
- package/src/mcp/McpToolName.js.map +1 -0
package/README.md
CHANGED
|
@@ -156,8 +156,10 @@ Apache-2.0
|
|
|
156
156
|
|
|
157
157
|
### MCP profile membership
|
|
158
158
|
|
|
159
|
-
`Mcp.DEFAULT` is the reserved base tool group. `@WpMcpTool(name
|
|
160
|
-
`profiles` option select only that group.
|
|
159
|
+
`Mcp.DEFAULT` is the reserved base tool group. `@WpMcpTool(name)` and an omitted
|
|
160
|
+
`profiles` option select only that group. The decorator takes the stable tool name only: the tool's
|
|
161
|
+
title is the method's `@summary` JSDoc tag (#1296), and a name must be 1-64 characters of
|
|
162
|
+
`[A-Za-z0-9_-]` (`McpToolName`) or the decorator throws. `{ profiles: ['admin'] }` selects only admin;
|
|
161
163
|
`{ profiles: [Mcp.DEFAULT, 'admin'] }` explicitly shares a tool between both groups. Membership is
|
|
162
164
|
independent of `@WpAuthorization` roles and never grants account permissions. Identifiers match
|
|
163
165
|
`[a-z][a-z0-9-]{0,63}`; empty lists, duplicates and malformed options are rejected.
|
package/package.json
CHANGED
package/src/http/api-type.js
CHANGED
|
@@ -102,8 +102,8 @@ function assertApiTypeMatchesMcpTools(apiClass) {
|
|
|
102
102
|
'every method is permanently outside MCP does not feed the MCP document.');
|
|
103
103
|
}
|
|
104
104
|
throw new Error(`${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +
|
|
105
|
-
"Add @WpMcpTool('<stable_tool_name>'
|
|
106
|
-
'
|
|
105
|
+
"Add @WpMcpTool('<stable_tool_name>') to the methods agents may call (each one's " +
|
|
106
|
+
'@summary JSDoc line is its title), or drop MCP from the @ApiType list.');
|
|
107
107
|
}
|
|
108
108
|
if (!declaresMcp && tools.length > 0) {
|
|
109
109
|
const named = tools.map((tool) => tool.methodName).join(', ');
|
package/src/http/api-type.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-type.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/api-type.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from './decorators';\nimport {\n InvalidEndpointForMcpMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n WpMcpToolMetadata,\n} from '../mcp/McpMetadata';\n\n/** Nominal backing keeps raw string literals out of contract declarations. */\nenum ApiTypeValue {\n SVC_TO_SVC = 'svc-to-svc',\n EXTERNAL_CUSTOMER = 'external-customer',\n MCP = 'mcp',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const SVC_TO_SVC = ApiTypeValue.SVC_TO_SVC;\nexport const EXTERNAL_CUSTOMER = ApiTypeValue.EXTERNAL_CUSTOMER;\nexport const MCP = ApiTypeValue.MCP;\n\n/** WHICH generated documents a contract feeds. One value per document. */\nexport type ApiTypeKind = typeof SVC_TO_SVC | typeof EXTERNAL_CUSTOMER | typeof MCP;\n\n/** Metadata key for the contract-level declaration, parallel to {@link METADATA_KEYS}. */\nexport const API_TYPE_METADATA_KEY = 'webpieces:api-type';\n\n/** The fail-closed default: a contract that says nothing feeds only the internal document. */\nexport const DEFAULT_API_TYPES: readonly ApiTypeKind[] = [SVC_TO_SVC];\n\n/**\n * WHICH generated documents this CONTRACT feeds.\n *\n * ```typescript\n * @ApiPath('/stores')\n * @ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER) // private + customer documents\n * export abstract class StoreApi { ... }\n * ```\n *\n * | value | document |\n * |---|---|\n * | `SVC_TO_SVC` | `full-private-openapi.json` |\n * | `EXTERNAL_CUSTOMER` | `public-openapi.json` |\n * | `MCP` | `mcp-openapi.json` |\n *\n * A document is WRITTEN when at least one contract declares its type, and it contains the contracts\n * that declared it and no others. No contract declaring `MCP` therefore means no `mcp-openapi.json`\n * at all — there is no separate emptiness rule to keep in step with this one.\n *\n * ## Absent means `SVC_TO_SVC` only, and the level is why that is affordable\n *\n * `.claude/rules/no-backwards-compat.md` shim shape #5 rejects \"a widening that is an ABSENCE rather\n * than a token\": a default that publishes to customers would mean you reach them having typed\n * nothing, so the safe state requires an edit and the widest grant is free. Naming the type inverts\n * that — `grep -rn EXTERNAL_CUSTOMER` is the entire customer-facing surface on one screen.\n *\n * It is a CLASS-level declaration because that costs one token per contract rather than one per\n * method. A per-method audience list was the earlier design and was rejected on exactly that\n * ergonomic ground; the per-METHOD case that remains — a new endpoint on an already-published API —\n * is served by `{ hidden: true }` in `EndpointOptions`, which subtracts one method from the customer\n * document and leaves it in the other two. The two are orthogonal: this one selects FILES, that one\n * subtracts a METHOD from one file.\n *\n * ## `MCP` and `@WpMcpTool` are ONE decision\n *\n * `MCP` on a contract with no `@WpMcpTool` method is an error, and a `@WpMcpTool` method on a\n * contract that does not declare `MCP` is an error. Both directions are asserted by\n * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they\n * can disagree, which is the defect this whole documentation epic exists to remove.\n *\n * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable\n * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY\n * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function ApiType(first: ApiTypeKind, ...rest: readonly ApiTypeKind[]): ClassDecorator {\n const types = [first, ...rest];\n return (target: Function) => {\n Reflect.defineMetadata(API_TYPE_METADATA_KEY, types, target);\n };\n}\n\n/** The documents this contract feeds — its declaration, else {@link DEFAULT_API_TYPES}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getApiTypes(apiClass: Function): readonly ApiTypeKind[] {\n const declared: ApiTypeKind[] | undefined = Reflect.getMetadata(\n API_TYPE_METADATA_KEY,\n apiClass,\n );\n return declared ?? DEFAULT_API_TYPES;\n}\n\n/**\n * MCP membership and `@WpMcpTool` must AGREE, in both directions.\n *\n * A wiring-time assert rather than a type, because the two declarations sit on different decorators\n * at different levels and TypeScript cannot relate them. It is the sibling of\n * `assertEveryEndpointHasAuthMode`, and exists for the same reason.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertApiTypeMatchesMcpTools(apiClass: Function): void {\n const declaresMcp = getApiTypes(apiClass).includes(MCP);\n const tools = getWpMcpTools(apiClass);\n const excluded = getInvalidEndpointsForMcp(apiClass);\n assertNoToolIsAlsoExcluded(apiClass, tools, excluded);\n if (declaresMcp && tools.length === 0) {\n // The all-excluded case gets its own sentence because its cure is the opposite one: there is\n // nothing to add, and the contract should stop declaring MCP.\n if (excluded.length > 0 && excluded.length === getApiTypeEndpointNames(apiClass).length) {\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but EVERY endpoint on it is ` +\n '@InvalidEndpointForMcp. Drop MCP from the @ApiType list — a contract whose ' +\n 'every method is permanently outside MCP does not feed the MCP document.',\n );\n }\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +\n \"Add @WpMcpTool('<stable_tool_name>', '<Human-readable title>') to the methods \" +\n 'agents may call, or drop MCP from the @ApiType list.',\n );\n }\n if (!declaresMcp && tools.length > 0) {\n const named = tools.map((tool: WpMcpToolMetadata) => tool.methodName).join(', ');\n throw new Error(\n `${apiClass.name} has @WpMcpTool on ${named} but does not declare @ApiType(..., MCP). ` +\n 'Add MCP to the @ApiType list — membership has ONE spelling, so a tool on a ' +\n 'contract nobody published to agents is a contradiction, not a hint.',\n );\n }\n}\n\n/**\n * `@WpMcpTool` and `@InvalidEndpointForMcp` on ONE method is a contradiction, not a precedence rule.\n *\n * Resolving it either way would make the pair a second spelling of a decision that already has one —\n * shim shape #1 in `.claude/rules/no-backwards-compat.md` — and whichever way it resolved, half the\n * readers of that method would be wrong about what it does.\n */\n// webpieces-disable no-function-outside-class -- private assert of assertApiTypeMatchesMcpTools, beside it\nfunction assertNoToolIsAlsoExcluded(\n apiClass: Function,\n tools: readonly WpMcpToolMetadata[],\n excluded: readonly InvalidEndpointForMcpMetadata[],\n): void {\n const excludedNames = new Set(\n excluded.map((one: InvalidEndpointForMcpMetadata) => one.methodName),\n );\n const both = tools\n .filter((tool: WpMcpToolMetadata) => excludedNames.has(tool.methodName))\n .map((tool: WpMcpToolMetadata) => tool.methodName);\n if (both.length === 0) return;\n throw new Error(\n `${apiClass.name} carries BOTH @WpMcpTool and @InvalidEndpointForMcp on ${both.join(', ')}. ` +\n 'They contradict each other — one publishes the method to agents and the other declares ' +\n 'it permanently unusable as a tool. Delete whichever one is wrong.',\n );\n}\n\n/** Reads the endpoints map so a caller can see which methods exist without importing decorators.ts. */\n// webpieces-disable no-function-outside-class -- metadata reader, sibling of getApiTypes\nexport function getApiTypeEndpointNames(apiClass: Function): readonly string[] {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass) ?? {};\n return Object.keys(endpoints);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"api-type.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/api-type.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from './decorators';\nimport {\n InvalidEndpointForMcpMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n WpMcpToolMetadata,\n} from '../mcp/McpMetadata';\n\n/** Nominal backing keeps raw string literals out of contract declarations. */\nenum ApiTypeValue {\n SVC_TO_SVC = 'svc-to-svc',\n EXTERNAL_CUSTOMER = 'external-customer',\n MCP = 'mcp',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const SVC_TO_SVC = ApiTypeValue.SVC_TO_SVC;\nexport const EXTERNAL_CUSTOMER = ApiTypeValue.EXTERNAL_CUSTOMER;\nexport const MCP = ApiTypeValue.MCP;\n\n/** WHICH generated documents a contract feeds. One value per document. */\nexport type ApiTypeKind = typeof SVC_TO_SVC | typeof EXTERNAL_CUSTOMER | typeof MCP;\n\n/** Metadata key for the contract-level declaration, parallel to {@link METADATA_KEYS}. */\nexport const API_TYPE_METADATA_KEY = 'webpieces:api-type';\n\n/** The fail-closed default: a contract that says nothing feeds only the internal document. */\nexport const DEFAULT_API_TYPES: readonly ApiTypeKind[] = [SVC_TO_SVC];\n\n/**\n * WHICH generated documents this CONTRACT feeds.\n *\n * ```typescript\n * @ApiPath('/stores')\n * @ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER) // private + customer documents\n * export abstract class StoreApi { ... }\n * ```\n *\n * | value | document |\n * |---|---|\n * | `SVC_TO_SVC` | `full-private-openapi.json` |\n * | `EXTERNAL_CUSTOMER` | `public-openapi.json` |\n * | `MCP` | `mcp-openapi.json` |\n *\n * A document is WRITTEN when at least one contract declares its type, and it contains the contracts\n * that declared it and no others. No contract declaring `MCP` therefore means no `mcp-openapi.json`\n * at all — there is no separate emptiness rule to keep in step with this one.\n *\n * ## Absent means `SVC_TO_SVC` only, and the level is why that is affordable\n *\n * `.claude/rules/no-backwards-compat.md` shim shape #5 rejects \"a widening that is an ABSENCE rather\n * than a token\": a default that publishes to customers would mean you reach them having typed\n * nothing, so the safe state requires an edit and the widest grant is free. Naming the type inverts\n * that — `grep -rn EXTERNAL_CUSTOMER` is the entire customer-facing surface on one screen.\n *\n * It is a CLASS-level declaration because that costs one token per contract rather than one per\n * method. A per-method audience list was the earlier design and was rejected on exactly that\n * ergonomic ground; the per-METHOD case that remains — a new endpoint on an already-published API —\n * is served by `{ hidden: true }` in `EndpointOptions`, which subtracts one method from the customer\n * document and leaves it in the other two. The two are orthogonal: this one selects FILES, that one\n * subtracts a METHOD from one file.\n *\n * ## `MCP` and `@WpMcpTool` are ONE decision\n *\n * `MCP` on a contract with no `@WpMcpTool` method is an error, and a `@WpMcpTool` method on a\n * contract that does not declare `MCP` is an error. Both directions are asserted by\n * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they\n * can disagree, which is the defect this whole documentation epic exists to remove.\n *\n * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable\n * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY\n * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function ApiType(first: ApiTypeKind, ...rest: readonly ApiTypeKind[]): ClassDecorator {\n const types = [first, ...rest];\n return (target: Function) => {\n Reflect.defineMetadata(API_TYPE_METADATA_KEY, types, target);\n };\n}\n\n/** The documents this contract feeds — its declaration, else {@link DEFAULT_API_TYPES}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getApiTypes(apiClass: Function): readonly ApiTypeKind[] {\n const declared: ApiTypeKind[] | undefined = Reflect.getMetadata(\n API_TYPE_METADATA_KEY,\n apiClass,\n );\n return declared ?? DEFAULT_API_TYPES;\n}\n\n/**\n * MCP membership and `@WpMcpTool` must AGREE, in both directions.\n *\n * A wiring-time assert rather than a type, because the two declarations sit on different decorators\n * at different levels and TypeScript cannot relate them. It is the sibling of\n * `assertEveryEndpointHasAuthMode`, and exists for the same reason.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertApiTypeMatchesMcpTools(apiClass: Function): void {\n const declaresMcp = getApiTypes(apiClass).includes(MCP);\n const tools = getWpMcpTools(apiClass);\n const excluded = getInvalidEndpointsForMcp(apiClass);\n assertNoToolIsAlsoExcluded(apiClass, tools, excluded);\n if (declaresMcp && tools.length === 0) {\n // The all-excluded case gets its own sentence because its cure is the opposite one: there is\n // nothing to add, and the contract should stop declaring MCP.\n if (excluded.length > 0 && excluded.length === getApiTypeEndpointNames(apiClass).length) {\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but EVERY endpoint on it is ` +\n '@InvalidEndpointForMcp. Drop MCP from the @ApiType list — a contract whose ' +\n 'every method is permanently outside MCP does not feed the MCP document.',\n );\n }\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +\n \"Add @WpMcpTool('<stable_tool_name>') to the methods agents may call (each one's \" +\n '@summary JSDoc line is its title), or drop MCP from the @ApiType list.',\n );\n }\n if (!declaresMcp && tools.length > 0) {\n const named = tools.map((tool: WpMcpToolMetadata) => tool.methodName).join(', ');\n throw new Error(\n `${apiClass.name} has @WpMcpTool on ${named} but does not declare @ApiType(..., MCP). ` +\n 'Add MCP to the @ApiType list — membership has ONE spelling, so a tool on a ' +\n 'contract nobody published to agents is a contradiction, not a hint.',\n );\n }\n}\n\n/**\n * `@WpMcpTool` and `@InvalidEndpointForMcp` on ONE method is a contradiction, not a precedence rule.\n *\n * Resolving it either way would make the pair a second spelling of a decision that already has one —\n * shim shape #1 in `.claude/rules/no-backwards-compat.md` — and whichever way it resolved, half the\n * readers of that method would be wrong about what it does.\n */\n// webpieces-disable no-function-outside-class -- private assert of assertApiTypeMatchesMcpTools, beside it\nfunction assertNoToolIsAlsoExcluded(\n apiClass: Function,\n tools: readonly WpMcpToolMetadata[],\n excluded: readonly InvalidEndpointForMcpMetadata[],\n): void {\n const excludedNames = new Set(\n excluded.map((one: InvalidEndpointForMcpMetadata) => one.methodName),\n );\n const both = tools\n .filter((tool: WpMcpToolMetadata) => excludedNames.has(tool.methodName))\n .map((tool: WpMcpToolMetadata) => tool.methodName);\n if (both.length === 0) return;\n throw new Error(\n `${apiClass.name} carries BOTH @WpMcpTool and @InvalidEndpointForMcp on ${both.join(', ')}. ` +\n 'They contradict each other — one publishes the method to agents and the other declares ' +\n 'it permanently unusable as a tool. Delete whichever one is wrong.',\n );\n}\n\n/** Reads the endpoints map so a caller can see which methods exist without importing decorators.ts. */\n// webpieces-disable no-function-outside-class -- metadata reader, sibling of getApiTypes\nexport function getApiTypeEndpointNames(apiClass: Function): readonly string[] {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass) ?? {};\n return Object.keys(endpoints);\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export type { EndpointKind, EndpointOperation, EndpointOptions, ExternalEndpoint
|
|
|
40
40
|
export { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';
|
|
41
41
|
export { InvalidEndpointForMcp, InvalidEndpointForMcpMetadata, WpMcpTool, WpMcpToolHints, WpMcpToolMetadata, getInvalidEndpointsForMcp, getWpMcpTools, mcpHintsForOperation, } from './mcp/McpMetadata';
|
|
42
42
|
export type { WpMcpMethodDecorator } from './mcp/McpMetadata';
|
|
43
|
+
export { McpToolName } from './mcp/McpToolName';
|
|
43
44
|
export { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaValidator, DtoValidationFailure, ObjectSchemaBuilder, } from './mcp/DtoSchema';
|
|
44
45
|
export type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';
|
|
45
46
|
export { McpToolCatalogFile, McpToolCatalogError, McpToolDefinition, } from './mcp/McpToolCatalogFile';
|
package/src/index.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType = exports.NUMERIC_METADATA_KEYS = exports.WpMax = exports.WpMin = exports.WpInt = exports.getHttpParameterDeclarations = exports.QueryParam = exports.PathParam = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getMaskSpec = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOperation = exports.getEndpointOptions = exports.getEndpointHttpMethod = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.isLocalOnly = exports.WpLocalOnly = exports.MISSING_AUTH_DECORATOR_FIX = exports.WpAuth = exports.WpAuthPublic = exports.Endpoint = exports.ApiPath = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = exports.LogChunkInfo = exports.LogChunkerImpl = exports.LogChunker = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
|
|
12
12
|
exports.InvalidEndpointForMcp = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.POST = exports.GET = exports.registerStreamingSchemas = exports.getStreamingEndpoint = exports.WpStream = exports.StreamDirection = exports.StreamWriter = exports.StreamTransportError = exports.StreamEventValidator = exports.StreamingEndpointMetadata = exports.StreamEnvelope = exports.StreamCorrelation = exports.HttpParameterDeclaration = exports.HttpParameterBinding = exports.HttpContractMapper = exports.BoundHttpRequest = exports.StreamDisconnectedError = exports.StreamProtocolError = exports.StreamErrorControl = exports.registerStreamingCatalog = exports.StreamingSchemaCatalog = exports.getAuthorization = exports.WpAuthorization = exports.AuthorizationDeclaration = exports.AuthorizationType = exports.RouteMetadataFactory = exports.RouteMetadata = exports.IPC_METADATA_KEYS = exports.assertNotInternalApi = exports.assertInternalApi = exports.getIpcMaskSpec = exports.getIpcEndpointKind = exports.getIpcEndpoints = exports.getIpcApiId = exports.isInternalApi = exports.WpIpcEndpoint = exports.WpInternal = exports.assertApiTypeMatchesMcpTools = exports.getApiTypeEndpointNames = exports.getApiTypes = exports.DEFAULT_API_TYPES = exports.API_TYPE_METADATA_KEY = void 0;
|
|
13
|
-
exports.
|
|
14
|
-
exports.
|
|
15
|
-
exports.McpToolProfilesOptions = exports.Mcp = exports.OAuthAuthorizationErrorRedirect = exports.OAuthErrorResponse = exports.OAuthErrorBody = exports.OAuthBearerChallenge = exports.OAuthErrorExtras = exports.OAuthProtocolError = exports.OAuthText = exports.OAuthErrorStatus = exports.OAuthErrorCode = exports.ApiErrorHttpStatus = exports.RpcCallFailed = exports.ReportableApiFailure = exports.CallModeMarker = exports.CallMode = exports.ApiCallSite = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ClientRole = exports.EndUserErrorRegistry = exports.EndUserErrorCode = exports.ApiEndUserBadRequestError = exports.ApiEndUserForbiddenError = exports.ApiEndUserNotFoundError = exports.ApiClientTooOldError = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiRetriesExhaustedError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = void 0;
|
|
13
|
+
exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.apiKey = exports.webhook = exports.sharedSecret = exports.oidcSvcs = exports.oidcSameProject = exports.jwt = exports.AuthDeclaration = exports.AuthMeta = exports.GenerateOutputTarget = exports.GenerateOutputLookup = exports.GenerateLayoutProblem = exports.GeneratedApiDocsLayout = exports.McpToolDefinition = exports.McpToolCatalogError = exports.McpToolCatalogFile = exports.ObjectSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchemaValidator = exports.ApiJsonSchemaDiscriminator = exports.ApiJsonSchema = exports.McpToolName = exports.mcpHintsForOperation = exports.getWpMcpTools = exports.getInvalidEndpointsForMcp = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.InvalidEndpointForMcpMetadata = void 0;
|
|
14
|
+
exports.ApiError = exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_ERROR_CLASSIFIER = exports.WebpiecesDefaultErrorClassifier = exports.KeyedErrorClassifier = exports.CallOutcome = exports.ReceivedApiErrorRule = exports.SurfaceEndUserStatus = exports.WEBPIECES_DEFAULT_ERROR_TRANSLATOR = exports.WebpiecesDefaultErrorTranslator = exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = exports.RuntimeLocality = exports.ServiceInfo = exports.ApiCallTimeoutError = exports.CallContext = exports.CallDeadline = exports.RetryStrategy = exports.TransportDefault = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = void 0;
|
|
15
|
+
exports.McpToolProfilesOptions = exports.Mcp = exports.OAuthAuthorizationErrorRedirect = exports.OAuthErrorResponse = exports.OAuthErrorBody = exports.OAuthBearerChallenge = exports.OAuthErrorExtras = exports.OAuthProtocolError = exports.OAuthText = exports.OAuthErrorStatus = exports.OAuthErrorCode = exports.ApiErrorHttpStatus = exports.RpcCallFailed = exports.ReportableApiFailure = exports.CallModeMarker = exports.CallMode = exports.ApiCallSite = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ClientRole = exports.EndUserErrorRegistry = exports.EndUserErrorCode = exports.ApiEndUserBadRequestError = exports.ApiEndUserForbiddenError = exports.ApiEndUserNotFoundError = exports.ApiClientTooOldError = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiRetriesExhaustedError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = void 0;
|
|
16
16
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
17
17
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
18
18
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -154,6 +154,8 @@ Object.defineProperty(exports, "WpMcpToolMetadata", { enumerable: true, get: fun
|
|
|
154
154
|
Object.defineProperty(exports, "getInvalidEndpointsForMcp", { enumerable: true, get: function () { return McpMetadata_1.getInvalidEndpointsForMcp; } });
|
|
155
155
|
Object.defineProperty(exports, "getWpMcpTools", { enumerable: true, get: function () { return McpMetadata_1.getWpMcpTools; } });
|
|
156
156
|
Object.defineProperty(exports, "mcpHintsForOperation", { enumerable: true, get: function () { return McpMetadata_1.mcpHintsForOperation; } });
|
|
157
|
+
var McpToolName_1 = require("./mcp/McpToolName");
|
|
158
|
+
Object.defineProperty(exports, "McpToolName", { enumerable: true, get: function () { return McpToolName_1.McpToolName; } });
|
|
157
159
|
var DtoSchema_1 = require("./mcp/DtoSchema");
|
|
158
160
|
Object.defineProperty(exports, "ApiJsonSchema", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchema; } });
|
|
159
161
|
Object.defineProperty(exports, "ApiJsonSchemaDiscriminator", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchemaDiscriminator; } });
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta, AuthDeclaration, jwt, oidcSameProject, oidcSvcs, sharedSecret, webhook, apiKey } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n OidcAuthMethod,\n OidcAuthMethods,\n OidcCallers,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport type { CallScope } from './http/CallRegistry';\nexport { TransportDefault } from './http/TransportDefault';\nexport { RetryStrategy } from './http/RetryStrategy';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client error classification: one CallOutcome (EXPECTED / TRANSIENT_FAILURE /\n// FAILURE) per thrown API-call error. Registered on ClientRegistry at startup; read by RetryStrategy,\n// CallRegistry's per-attempt log line, LogApiCall and the MCP renderer's retry advice.\nexport type { ErrorClassifier } from './http/ErrorClassifier';\nexport { CallOutcome, KeyedErrorClassifier } from './http/ErrorClassifier';\nexport {\n WebpiecesDefaultErrorClassifier,\n WEBPIECES_DEFAULT_ERROR_CLASSIFIER,\n} from './http/WebpiecesDefaultErrorClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiRetriesExhaustedError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n EndUserErrorCode,\n EndUserErrorRegistry,\n ClientRole,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n ApiCallSite,\n CallMode,\n CallModeMarker,\n ReportableApiFailure,\n RpcCallFailed,\n} from './errors';\nexport type {\n ApiErrorKind,\n ApiStatusCode,\n EdgeHttpStatus,\n EndUserErrorFactory,\n CallSiteBoundary,\n} from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\n\n// RFC 6749 / 6750 / 7591 OAuth refusals for app-hosted OAuth endpoints (#1176): the typed error,\n// its one renderer, the Bearer challenge builder and the authorization-endpoint error redirect.\nexport { OAuthErrorCode, OAuthErrorStatus, OAuthText } from './oauth/OAuthErrorCode';\nexport type {\n OAuthHttpStatus,\n InvalidClientStatus,\n OAuthFixedStatusErrorCode,\n BearerErrorCode,\n AuthorizationEndpointErrorCode,\n} from './oauth/OAuthErrorCode';\nexport { OAuthProtocolError, OAuthErrorExtras } from './oauth/OAuthProtocolError';\nexport { OAuthBearerChallenge } from './oauth/OAuthBearerChallenge';\nexport { OAuthErrorBody, OAuthErrorResponse } from './oauth/OAuthErrorResponse';\nexport { OAuthAuthorizationErrorRedirect } from './oauth/OAuthAuthorizationErrorRedirect';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n\nexport { Mcp, McpToolProfilesOptions } from './mcp/Mcp';\nexport type { McpToolProfiles } from './mcp/Mcp';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport { McpToolName } from './mcp/McpToolName';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta, AuthDeclaration, jwt, oidcSameProject, oidcSvcs, sharedSecret, webhook, apiKey } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n OidcAuthMethod,\n OidcAuthMethods,\n OidcCallers,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport type { CallScope } from './http/CallRegistry';\nexport { TransportDefault } from './http/TransportDefault';\nexport { RetryStrategy } from './http/RetryStrategy';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client error classification: one CallOutcome (EXPECTED / TRANSIENT_FAILURE /\n// FAILURE) per thrown API-call error. Registered on ClientRegistry at startup; read by RetryStrategy,\n// CallRegistry's per-attempt log line, LogApiCall and the MCP renderer's retry advice.\nexport type { ErrorClassifier } from './http/ErrorClassifier';\nexport { CallOutcome, KeyedErrorClassifier } from './http/ErrorClassifier';\nexport {\n WebpiecesDefaultErrorClassifier,\n WEBPIECES_DEFAULT_ERROR_CLASSIFIER,\n} from './http/WebpiecesDefaultErrorClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiRetriesExhaustedError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n EndUserErrorCode,\n EndUserErrorRegistry,\n ClientRole,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n ApiCallSite,\n CallMode,\n CallModeMarker,\n ReportableApiFailure,\n RpcCallFailed,\n} from './errors';\nexport type {\n ApiErrorKind,\n ApiStatusCode,\n EdgeHttpStatus,\n EndUserErrorFactory,\n CallSiteBoundary,\n} from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\n\n// RFC 6749 / 6750 / 7591 OAuth refusals for app-hosted OAuth endpoints (#1176): the typed error,\n// its one renderer, the Bearer challenge builder and the authorization-endpoint error redirect.\nexport { OAuthErrorCode, OAuthErrorStatus, OAuthText } from './oauth/OAuthErrorCode';\nexport type {\n OAuthHttpStatus,\n InvalidClientStatus,\n OAuthFixedStatusErrorCode,\n BearerErrorCode,\n AuthorizationEndpointErrorCode,\n} from './oauth/OAuthErrorCode';\nexport { OAuthProtocolError, OAuthErrorExtras } from './oauth/OAuthProtocolError';\nexport { OAuthBearerChallenge } from './oauth/OAuthBearerChallenge';\nexport { OAuthErrorBody, OAuthErrorResponse } from './oauth/OAuthErrorResponse';\nexport { OAuthAuthorizationErrorRedirect } from './oauth/OAuthAuthorizationErrorRedirect';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n\nexport { Mcp, McpToolProfilesOptions } from './mcp/Mcp';\nexport type { McpToolProfiles } from './mcp/Mcp';\n"]}
|
|
@@ -9,12 +9,16 @@ const McpMetadata_1 = require("./McpMetadata");
|
|
|
9
9
|
* with TS2578 the moment one of these starts compiling.
|
|
10
10
|
*/
|
|
11
11
|
class InvalidMcpContract {
|
|
12
|
-
// @ts-expect-error @WpMcpTool takes the stable protocol NAME
|
|
12
|
+
// @ts-expect-error @WpMcpTool takes the stable protocol NAME, not an options object
|
|
13
13
|
options(_request) {
|
|
14
14
|
throw new Error('contract only');
|
|
15
15
|
}
|
|
16
|
-
// @ts-expect-error the title is
|
|
17
|
-
|
|
16
|
+
// @ts-expect-error the title argument is GONE (#1296) — the title is the method's summary JSDoc tag
|
|
17
|
+
titled(_request) {
|
|
18
|
+
throw new Error('contract only');
|
|
19
|
+
}
|
|
20
|
+
// @ts-expect-error a title argument is gone even when the profiles object follows it (#1296)
|
|
21
|
+
titledWithProfiles(_request) {
|
|
18
22
|
throw new Error('contract only');
|
|
19
23
|
}
|
|
20
24
|
// @ts-expect-error side-effect claims belong on @Endpoint.operation, never @WpMcpTool
|
|
@@ -29,13 +33,19 @@ tslib_1.__decorate([
|
|
|
29
33
|
tslib_1.__metadata("design:returntype", Promise)
|
|
30
34
|
], InvalidMcpContract.prototype, "options", null);
|
|
31
35
|
tslib_1.__decorate([
|
|
32
|
-
(0, McpMetadata_1.WpMcpTool)('
|
|
36
|
+
(0, McpMetadata_1.WpMcpTool)('titled', 'A title argument'),
|
|
37
|
+
tslib_1.__metadata("design:type", Function),
|
|
38
|
+
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
39
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
40
|
+
], InvalidMcpContract.prototype, "titled", null);
|
|
41
|
+
tslib_1.__decorate([
|
|
42
|
+
(0, McpMetadata_1.WpMcpTool)('titled_with_profiles', 'A title argument', { profiles: ['admin'] }),
|
|
33
43
|
tslib_1.__metadata("design:type", Function),
|
|
34
44
|
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
35
45
|
tslib_1.__metadata("design:returntype", Promise)
|
|
36
|
-
], InvalidMcpContract.prototype, "
|
|
46
|
+
], InvalidMcpContract.prototype, "titledWithProfiles", null);
|
|
37
47
|
tslib_1.__decorate([
|
|
38
|
-
(0, McpMetadata_1.WpMcpTool)('invalid',
|
|
48
|
+
(0, McpMetadata_1.WpMcpTool)('invalid', true),
|
|
39
49
|
tslib_1.__metadata("design:type", Function),
|
|
40
50
|
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
41
51
|
tslib_1.__metadata("design:returntype", Promise)
|
|
@@ -55,19 +65,19 @@ class InvalidMcpArities {
|
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
tslib_1.__decorate([
|
|
58
|
-
(0, McpMetadata_1.WpMcpTool)('empty_profiles',
|
|
68
|
+
(0, McpMetadata_1.WpMcpTool)('empty_profiles', { profiles: [] }),
|
|
59
69
|
tslib_1.__metadata("design:type", Function),
|
|
60
70
|
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
61
71
|
tslib_1.__metadata("design:returntype", Promise)
|
|
62
72
|
], InvalidMcpArities.prototype, "emptyProfiles", null);
|
|
63
73
|
tslib_1.__decorate([
|
|
64
|
-
(0, McpMetadata_1.WpMcpTool)('zero-arity'
|
|
74
|
+
(0, McpMetadata_1.WpMcpTool)('zero-arity'),
|
|
65
75
|
tslib_1.__metadata("design:type", Function),
|
|
66
76
|
tslib_1.__metadata("design:paramtypes", []),
|
|
67
77
|
tslib_1.__metadata("design:returntype", Promise)
|
|
68
78
|
], InvalidMcpArities.prototype, "zero", null);
|
|
69
79
|
tslib_1.__decorate([
|
|
70
|
-
(0, McpMetadata_1.WpMcpTool)('multi-arity'
|
|
80
|
+
(0, McpMetadata_1.WpMcpTool)('multi-arity'),
|
|
71
81
|
tslib_1.__metadata("design:type", Function),
|
|
72
82
|
tslib_1.__metadata("design:paramtypes", [Object, Object]),
|
|
73
83
|
tslib_1.__metadata("design:returntype", Promise)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,+CAA0C;AAE1C;;;;;GAKG;AACH,MAAe,kBAAkB;IAC7B,
|
|
1
|
+
{"version":3,"file":"McpCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,+CAA0C;AAE1C;;;;;GAKG;AACH,MAAe,kBAAkB;IAC7B,oFAAoF;IAEpF,OAAO,CAAC,QAAgB;QACpB,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,oGAAoG;IAEpG,MAAM,CAAC,QAAgB;QACnB,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,6FAA6F;IAE7F,kBAAkB,CAAC,QAAgB;QAC/B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,sFAAsF;IAEtF,KAAK,CAAC,QAAgB;QAClB,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACJ;AArBG;IADC,IAAA,uBAAS,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;;;iDAG9B;AAID;IADC,IAAA,uBAAS,EAAC,QAAQ,EAAE,kBAAkB,CAAC;;;;gDAGvC;AAID;IADC,IAAA,uBAAS,EAAC,sBAAsB,EAAE,kBAAkB,EAAE,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;;;;4DAG9E;AAID;IADC,IAAA,uBAAS,EAAC,SAAS,EAAE,IAAI,CAAC;;;;+CAG1B;AAGL,MAAe,iBAAiB;IAC5B,yFAAyF;IAEzF,aAAa,CAAC,QAAgB;QAC1B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,6DAA6D;IAE7D,IAAI;QACA,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,6DAA6D;IAE7D,KAAK,CAAC,MAAc,EAAE,OAAe;QACjC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACJ;AAfG;IADC,IAAA,uBAAS,EAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;;;;sDAG7C;AAID;IADC,IAAA,uBAAS,EAAC,YAAY,CAAC;;;;6CAGvB;AAID;IADC,IAAA,uBAAS,EAAC,aAAa,CAAC;;;;8CAGxB;AAGL,KAAK,kBAAkB,CAAC;AACxB,KAAK,iBAAiB,CAAC","sourcesContent":["import { WpMcpTool } from './McpMetadata';\n\n/**\n * Compile-time pins for `@WpMcpTool`. These live in a COMPILED file rather than a `.spec.ts` because\n * vitest strips types with esbuild and `tsconfig.lib.json` excludes specs, so a `@ts-expect-error` in\n * a spec is inert (`.claude/rules/no-backwards-compat.md` shim shape #4). Here, tsc fails the build\n * with TS2578 the moment one of these starts compiling.\n */\nabstract class InvalidMcpContract {\n // @ts-expect-error @WpMcpTool takes the stable protocol NAME, not an options object\n @WpMcpTool({ name: 'invalid' })\n options(_request: object): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error the title argument is GONE (#1296) — the title is the method's summary JSDoc tag\n @WpMcpTool('titled', 'A title argument')\n titled(_request: object): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error a title argument is gone even when the profiles object follows it (#1296)\n @WpMcpTool('titled_with_profiles', 'A title argument', { profiles: ['admin'] })\n titledWithProfiles(_request: object): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error side-effect claims belong on @Endpoint.operation, never @WpMcpTool\n @WpMcpTool('invalid', true)\n hints(_request: object): Promise<object> {\n throw new Error('contract only');\n }\n}\n\nabstract class InvalidMcpArities {\n // @ts-expect-error explicit profile membership is nonempty; omit options for Mcp.DEFAULT\n @WpMcpTool('empty_profiles', { profiles: [] })\n emptyProfiles(_request: object): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error MCP tools require exactly one request DTO\n @WpMcpTool('zero-arity')\n zero(): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error MCP tools require exactly one request DTO\n @WpMcpTool('multi-arity')\n multi(_first: object, _second: object): Promise<object> {\n throw new Error('contract only');\n }\n}\n\nvoid InvalidMcpContract;\nvoid InvalidMcpArities;\n"]}
|
package/src/mcp/McpMetadata.d.ts
CHANGED
|
@@ -18,47 +18,47 @@ export type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(target: o
|
|
|
18
18
|
export declare class WpMcpToolMetadata {
|
|
19
19
|
readonly methodName: string;
|
|
20
20
|
readonly name: string;
|
|
21
|
-
/** The human-readable display name; `tools/list` publishes it as `title` AND `annotations.title`. */
|
|
22
|
-
readonly title: string;
|
|
23
21
|
readonly profiles: readonly string[];
|
|
24
|
-
constructor(methodName: string, name: string,
|
|
25
|
-
/** The human-readable display name; `tools/list` publishes it as `title` AND `annotations.title`. */
|
|
26
|
-
title: string, profiles: readonly string[]);
|
|
22
|
+
constructor(methodName: string, name: string, profiles: readonly string[]);
|
|
27
23
|
}
|
|
28
24
|
/**
|
|
29
25
|
* Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under
|
|
30
|
-
* the STABLE protocol name
|
|
26
|
+
* the STABLE protocol name given here.
|
|
31
27
|
*
|
|
32
28
|
* ```typescript
|
|
33
|
-
*
|
|
29
|
+
* /**
|
|
30
|
+
* * Every passage the learner can open, newest first.
|
|
31
|
+
* *
|
|
32
|
+
* * @summary Get your passages
|
|
33
|
+
* * @returns The learner's passages, newest first.
|
|
34
|
+
* *\/
|
|
35
|
+
* @WpMcpTool('learner_get_passages')
|
|
34
36
|
* ```
|
|
35
37
|
*
|
|
36
|
-
* The optional
|
|
37
|
-
*
|
|
38
|
-
* state:
|
|
38
|
+
* The optional second argument selects profiles; omission selects only Mcp.DEFAULT. The name is the
|
|
39
|
+
* one fact the source cannot otherwise state:
|
|
39
40
|
*
|
|
40
41
|
* | fact | where it comes from |
|
|
41
42
|
* |---|---|
|
|
42
|
-
* | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow |
|
|
43
|
-
* | `title` |
|
|
43
|
+
* | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow. 1-64 characters of `[A-Za-z0-9_-]` ({@link McpToolName}) |
|
|
44
|
+
* | `title` | the method's REQUIRED `@summary` JSDoc tag — the same line OpenAPI publishes as the operation's `summary`; published as BOTH `Tool.title` and `annotations.title` |
|
|
44
45
|
* | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |
|
|
45
46
|
* | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |
|
|
46
47
|
* | `openWorldHint` | `@Endpoint`'s `openWorld` option |
|
|
47
48
|
* | `inputSchema` / `outputSchema` | the declared request and response types |
|
|
48
49
|
*
|
|
49
|
-
* ## Why the title is
|
|
50
|
+
* ## Why the title is NOT an argument here (#1296)
|
|
50
51
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* It used to be the second argument (#1180). That was a second place to write an endpoint's short
|
|
53
|
+
* title: it never reached OpenAPI, whose operation `summary` was the bare method name, and the two
|
|
54
|
+
* drifted. The title now has ONE source, the `@summary` JSDoc tag, which `wp-openapi` REQUIRES on every
|
|
55
|
+
* published operation and publishes as OpenAPI `summary`, MCP `title` and MCP `annotations.title`.
|
|
56
|
+
* Passing a title here is a compile error; the second parameter is the profiles object.
|
|
56
57
|
*
|
|
57
|
-
* Until #984 the decorator also took a `description`, duplicating the JSDoc
|
|
58
|
-
* one
|
|
59
|
-
* not a second copy of the description.
|
|
58
|
+
* Until #984 the decorator also took a `description`, duplicating the JSDoc, and was cut for the same
|
|
59
|
+
* reason: two authored copies of one fact drift the first time somebody edits one.
|
|
60
60
|
*/
|
|
61
|
-
export declare function WpMcpTool(name: string,
|
|
61
|
+
export declare function WpMcpTool(name: string, options?: McpToolProfilesOptions): WpMcpMethodDecorator;
|
|
62
62
|
/** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */
|
|
63
63
|
export declare class InvalidEndpointForMcpMetadata {
|
|
64
64
|
readonly methodName: string;
|
package/src/mcp/McpMetadata.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.mcpHintsForOperation = mcpHintsForOperation;
|
|
|
8
8
|
exports.getWpMcpTools = getWpMcpTools;
|
|
9
9
|
require("reflect-metadata");
|
|
10
10
|
const Mcp_1 = require("./Mcp");
|
|
11
|
+
const McpToolName_1 = require("./McpToolName");
|
|
11
12
|
const decorators_1 = require("../http/decorators");
|
|
12
13
|
const HttpEndpointOptions_1 = require("../http/HttpEndpointOptions");
|
|
13
14
|
/** The immutable hints MCP clients use when deciding whether and how to call a tool. */
|
|
@@ -28,73 +29,71 @@ exports.WpMcpToolHints = WpMcpToolHints;
|
|
|
28
29
|
class WpMcpToolMetadata {
|
|
29
30
|
methodName;
|
|
30
31
|
name;
|
|
31
|
-
title;
|
|
32
32
|
profiles;
|
|
33
|
-
constructor(methodName, name,
|
|
34
|
-
/** The human-readable display name; `tools/list` publishes it as `title` AND `annotations.title`. */
|
|
35
|
-
title, profiles) {
|
|
33
|
+
constructor(methodName, name, profiles) {
|
|
36
34
|
this.methodName = methodName;
|
|
37
35
|
this.name = name;
|
|
38
|
-
this.title = title;
|
|
39
36
|
this.profiles = profiles;
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
exports.WpMcpToolMetadata = WpMcpToolMetadata;
|
|
43
40
|
/**
|
|
44
41
|
* Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under
|
|
45
|
-
* the STABLE protocol name
|
|
42
|
+
* the STABLE protocol name given here.
|
|
46
43
|
*
|
|
47
44
|
* ```typescript
|
|
48
|
-
*
|
|
45
|
+
* /**
|
|
46
|
+
* * Every passage the learner can open, newest first.
|
|
47
|
+
* *
|
|
48
|
+
* * @summary Get your passages
|
|
49
|
+
* * @returns The learner's passages, newest first.
|
|
50
|
+
* *\/
|
|
51
|
+
* @WpMcpTool('learner_get_passages')
|
|
49
52
|
* ```
|
|
50
53
|
*
|
|
51
|
-
* The optional
|
|
52
|
-
*
|
|
53
|
-
* state:
|
|
54
|
+
* The optional second argument selects profiles; omission selects only Mcp.DEFAULT. The name is the
|
|
55
|
+
* one fact the source cannot otherwise state:
|
|
54
56
|
*
|
|
55
57
|
* | fact | where it comes from |
|
|
56
58
|
* |---|---|
|
|
57
|
-
* | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow |
|
|
58
|
-
* | `title` |
|
|
59
|
+
* | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow. 1-64 characters of `[A-Za-z0-9_-]` ({@link McpToolName}) |
|
|
60
|
+
* | `title` | the method's REQUIRED `@summary` JSDoc tag — the same line OpenAPI publishes as the operation's `summary`; published as BOTH `Tool.title` and `annotations.title` |
|
|
59
61
|
* | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |
|
|
60
62
|
* | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |
|
|
61
63
|
* | `openWorldHint` | `@Endpoint`'s `openWorld` option |
|
|
62
64
|
* | `inputSchema` / `outputSchema` | the declared request and response types |
|
|
63
65
|
*
|
|
64
|
-
* ## Why the title is
|
|
66
|
+
* ## Why the title is NOT an argument here (#1296)
|
|
65
67
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* It used to be the second argument (#1180). That was a second place to write an endpoint's short
|
|
69
|
+
* title: it never reached OpenAPI, whose operation `summary` was the bare method name, and the two
|
|
70
|
+
* drifted. The title now has ONE source, the `@summary` JSDoc tag, which `wp-openapi` REQUIRES on every
|
|
71
|
+
* published operation and publishes as OpenAPI `summary`, MCP `title` and MCP `annotations.title`.
|
|
72
|
+
* Passing a title here is a compile error; the second parameter is the profiles object.
|
|
71
73
|
*
|
|
72
|
-
* Until #984 the decorator also took a `description`, duplicating the JSDoc
|
|
73
|
-
* one
|
|
74
|
-
* not a second copy of the description.
|
|
74
|
+
* Until #984 the decorator also took a `description`, duplicating the JSDoc, and was cut for the same
|
|
75
|
+
* reason: two authored copies of one fact drift the first time somebody edits one.
|
|
75
76
|
*/
|
|
76
77
|
// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope
|
|
77
|
-
function WpMcpTool(name,
|
|
78
|
+
function WpMcpTool(name, options) {
|
|
78
79
|
if (options !== undefined &&
|
|
79
80
|
(typeof options !== 'object' ||
|
|
80
81
|
options === null ||
|
|
81
82
|
Array.isArray(options) ||
|
|
82
83
|
Object.keys(options).some((key) => key !== 'profiles'))) {
|
|
83
|
-
throw new Error('@WpMcpTool options must be an object containing only profiles.'
|
|
84
|
+
throw new Error('@WpMcpTool options must be an object containing only profiles. The tool title is the ' +
|
|
85
|
+
"method's @summary JSDoc tag, never a decorator argument.");
|
|
84
86
|
}
|
|
85
87
|
const profiles = Mcp_1.Mcp.profiles(options !== undefined && 'profiles' in options ? options.profiles : [Mcp_1.Mcp.DEFAULT], '@WpMcpTool');
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error(`@WpMcpTool('${name}', ...) requires a non-empty human-readable title, e.g. ` +
|
|
90
|
-
"@WpMcpTool('learner_get_passages', 'Get your passages'). tools/list publishes it as " +
|
|
91
|
-
'title and annotations.title, and the Claude Connectors Directory flags a tool without one.');
|
|
88
|
+
const problem = McpToolName_1.McpToolName.problemWith(name);
|
|
89
|
+
if (problem !== undefined) {
|
|
90
|
+
throw new Error(`@WpMcpTool('${name}'): the tool name ${problem}. ${McpToolName_1.McpToolName.cure()}`);
|
|
92
91
|
}
|
|
93
92
|
return (target, propertyKey, _descriptor) => {
|
|
94
93
|
const apiClass = target.constructor;
|
|
95
94
|
const tools = Reflect.getMetadata(decorators_1.METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};
|
|
96
95
|
const methodName = String(propertyKey);
|
|
97
|
-
tools[methodName] = new WpMcpToolMetadata(methodName, name,
|
|
96
|
+
tools[methodName] = new WpMcpToolMetadata(methodName, name, profiles);
|
|
98
97
|
Reflect.defineMetadata(decorators_1.METADATA_KEYS.MCP_TOOLS, tools, apiClass);
|
|
99
98
|
};
|
|
100
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { Mcp, McpToolProfilesOptions } from './Mcp';\nimport { METADATA_KEYS } from '../http/decorators';\nimport { EndpointOperation, READ, WRITE_IDEMPOTENT, WRITE } from '../http/HttpEndpointOptions';\n\n/** The immutable hints MCP clients use when deciding whether and how to call a tool. */\nexport class WpMcpToolHints {\n constructor(\n public readonly readOnlyHint: boolean,\n public readonly destructiveHint: boolean,\n public readonly idempotentHint: boolean,\n public readonly openWorldHint: boolean,\n ) {}\n}\n\ntype AsyncObjectMethod = (...args: never[]) => Promise<object>;\ntype ExactOneParameter<TMethod extends AsyncObjectMethod> = Parameters<TMethod>['length'] extends 1\n ? object\n : never;\ntype ExactOneMethodDescriptor<TMethod extends AsyncObjectMethod> =\n TypedPropertyDescriptor<TMethod> & ExactOneParameter<TMethod>;\n\n/** Method decorator restricted to one request DTO and one async response DTO. */\nexport type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n descriptor: ExactOneMethodDescriptor<TMethod>,\n) => void;\n\n/** Runtime metadata for one explicitly exposed endpoint. */\nexport class WpMcpToolMetadata {\n constructor(\n public readonly methodName: string,\n public readonly name: string,\n /** The human-readable display name; `tools/list` publishes it as `title` AND `annotations.title`. */\n public readonly title: string,\n public readonly profiles: readonly string[],\n ) {}\n}\n\n/**\n * Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under\n * the STABLE protocol name and the human-readable TITLE given here.\n *\n * ```typescript\n * @WpMcpTool('learner_get_passages', 'Get your passages')\n * ```\n *\n * The optional third argument selects profiles; omission selects only Mcp.DEFAULT.\n * The name and title are facts the source cannot otherwise\n * state:\n *\n * | fact | where it comes from |\n * |---|---|\n * | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow |\n * | `title` | this decorator — the display name clients and the Claude Connectors Directory show; published as BOTH `Tool.title` and `annotations.title` |\n * | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |\n * | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |\n * | `openWorldHint` | `@Endpoint`'s `openWorld` option |\n * | `inputSchema` / `outputSchema` | the declared request and response types |\n *\n * ## Why the title is REQUIRED and never derived (#1180)\n *\n * The Connectors Directory flags every tool without `annotations.title`. An optional title gets\n * skipped and the miss surfaces only at directory-submission time, so it is a compile-time\n * requirement and an empty or whitespace one throws at decoration time. It is never computed from\n * the name: `learner_get_passages` → \"Learner get passages\" is exactly the mechanical listing text\n * reviewers push back on, and the area prefix means nothing to an end user.\n *\n * Until #984 the decorator also took a `description`, duplicating the JSDoc. Two authored copies of\n * one paragraph drift the first time somebody edits one, so it is gone; the title is a short NAME,\n * not a second copy of the description.\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpMcpTool(\n name: string,\n title: string,\n options?: McpToolProfilesOptions,\n): WpMcpMethodDecorator {\n if (\n options !== undefined &&\n (typeof options !== 'object' ||\n options === null ||\n Array.isArray(options) ||\n Object.keys(options).some((key: string) => key !== 'profiles'))\n ) {\n throw new Error('@WpMcpTool options must be an object containing only profiles.');\n }\n const profiles = Mcp.profiles(\n options !== undefined && 'profiles' in options ? options.profiles! : [Mcp.DEFAULT],\n '@WpMcpTool',\n );\n if (name.trim() === '') throw new Error('@WpMcpTool requires a non-empty stable name.');\n if (title.trim() === '') {\n throw new Error(\n `@WpMcpTool('${name}', ...) requires a non-empty human-readable title, e.g. ` +\n \"@WpMcpTool('learner_get_passages', 'Get your passages'). tools/list publishes it as \" +\n 'title and annotations.title, and the Claude Connectors Directory flags a tool without one.',\n );\n }\n return <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n _descriptor: ExactOneMethodDescriptor<TMethod>,\n ): void => {\n const apiClass = target.constructor;\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n const methodName = String(propertyKey);\n tools[methodName] = new WpMcpToolMetadata(methodName, name, title, profiles);\n Reflect.defineMetadata(METADATA_KEYS.MCP_TOOLS, tools, apiClass);\n };\n}\n\n/** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */\nexport class InvalidEndpointForMcpMetadata {\n constructor(\n public readonly methodName: string,\n /** WHY it can never be a tool. Non-empty, enforced at decoration time. */\n public readonly reason: string,\n ) {}\n}\n\n/**\n * Declares that this endpoint is PERMANENTLY outside MCP, and says why.\n *\n * ## It is not a suppression, and that distinction is the whole feature\n *\n * `// webpieces-disable api-rules-for-mcp -- <reason>` says \"stop complaining\". This says \"this is\n * structurally impossible, forever\", and the two have different futures: a suppression is something\n * somebody should eventually come back to, and this is something nobody should. Both measured cases\n * are types that are CORRECT and must not be \"fixed\" —\n *\n * - a webhook TRANSPORT envelope, whose `data` is owned by each event's own published contract;\n * typing it here would fork that ownership and make adding an event type a change to the\n * transport contract rather than a registration, and\n * - a freeform-SQL escape hatch, whose result columns depend on the model-authored `SELECT`.\n *\n * The second one is worth stating rather than burying: it is the most obviously useful tool in that\n * repo, and it is excluded by a type that is right.\n *\n * ## Absence already says \"not a tool YET\"\n *\n * A method with no `@WpMcpTool` is simply not published. This decorator is the NEVER, and the reason\n * is what makes `grep -rn InvalidEndpointForMcp` a list of permanent exclusions with their arguments\n * rather than a list of names. `api-rules-for-mcp` restates that list on every run, so an exclusion\n * stays visible instead of being announced once and forgotten.\n *\n * ## It contradicts `@WpMcpTool`\n *\n * Both on one method is an ERROR, asserted by {@link assertApiTypeMatchesMcpTools}. \"Publish this as\n * a tool\" and \"this can never be a tool\" are two spellings of one decision, which is exactly the\n * defect `.claude/rules/no-backwards-compat.md` shim shape #1 rejects.\n *\n * ```typescript\n * @InvalidEndpointForMcp('a transport envelope body is opaque by design; the published partner ' +\n * 'contract for each event type owns its shape')\n * ```\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function InvalidEndpointForMcp(reason: string): MethodDecorator {\n if (reason.trim() === '') {\n throw new Error(\n '@InvalidEndpointForMcp requires a non-empty reason. The reason IS the declaration — ' +\n 'without it this is a suppression with a longer name.',\n );\n }\n return (target: object, propertyKey: string | symbol): void => {\n const apiClass = target.constructor;\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n const methodName = String(propertyKey);\n excluded[methodName] = new InvalidEndpointForMcpMetadata(methodName, reason);\n Reflect.defineMetadata(METADATA_KEYS.MCP_INVALID, excluded, apiClass);\n };\n}\n\n/** Every method declared permanently outside MCP by {@link InvalidEndpointForMcp}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getInvalidEndpointsForMcp(apiClass: Function): InvalidEndpointForMcpMetadata[] {\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n return Object.values(excluded);\n}\n\n/** The one mapping from endpoint side effects to MCP advisory annotations. */\n// webpieces-disable no-function-outside-class -- pure metadata mapping shared by MCP registration\nexport function mcpHintsForOperation(\n operation: EndpointOperation,\n openWorldHint: boolean,\n): WpMcpToolHints {\n switch (operation) {\n case READ:\n return new WpMcpToolHints(true, false, true, openWorldHint);\n case WRITE_IDEMPOTENT:\n return new WpMcpToolHints(false, true, true, openWorldHint);\n case WRITE:\n return new WpMcpToolHints(false, true, false, openWorldHint);\n }\n}\n\n/** Returns only methods explicitly opted in through {@link WpMcpTool}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getWpMcpTools(apiClass: Function): WpMcpToolMetadata[] {\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n return Object.values(tools);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"McpMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { Mcp, McpToolProfilesOptions } from './Mcp';\nimport { McpToolName } from './McpToolName';\nimport { METADATA_KEYS } from '../http/decorators';\nimport { EndpointOperation, READ, WRITE_IDEMPOTENT, WRITE } from '../http/HttpEndpointOptions';\n\n/** The immutable hints MCP clients use when deciding whether and how to call a tool. */\nexport class WpMcpToolHints {\n constructor(\n public readonly readOnlyHint: boolean,\n public readonly destructiveHint: boolean,\n public readonly idempotentHint: boolean,\n public readonly openWorldHint: boolean,\n ) {}\n}\n\ntype AsyncObjectMethod = (...args: never[]) => Promise<object>;\ntype ExactOneParameter<TMethod extends AsyncObjectMethod> = Parameters<TMethod>['length'] extends 1\n ? object\n : never;\ntype ExactOneMethodDescriptor<TMethod extends AsyncObjectMethod> =\n TypedPropertyDescriptor<TMethod> & ExactOneParameter<TMethod>;\n\n/** Method decorator restricted to one request DTO and one async response DTO. */\nexport type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n descriptor: ExactOneMethodDescriptor<TMethod>,\n) => void;\n\n/** Runtime metadata for one explicitly exposed endpoint. */\nexport class WpMcpToolMetadata {\n constructor(\n public readonly methodName: string,\n public readonly name: string,\n public readonly profiles: readonly string[],\n ) {}\n}\n\n/**\n * Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under\n * the STABLE protocol name given here.\n *\n * ```typescript\n * /**\n * * Every passage the learner can open, newest first.\n * *\n * * @summary Get your passages\n * * @returns The learner's passages, newest first.\n * *\\/\n * @WpMcpTool('learner_get_passages')\n * ```\n *\n * The optional second argument selects profiles; omission selects only Mcp.DEFAULT. The name is the\n * one fact the source cannot otherwise state:\n *\n * | fact | where it comes from |\n * |---|---|\n * | `name` | this decorator — the stable protocol identity, independent of the method name because renaming a method must not break a saved agent workflow. 1-64 characters of `[A-Za-z0-9_-]` ({@link McpToolName}) |\n * | `title` | the method's REQUIRED `@summary` JSDoc tag — the same line OpenAPI publishes as the operation's `summary`; published as BOTH `Tool.title` and `annotations.title` |\n * | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |\n * | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |\n * | `openWorldHint` | `@Endpoint`'s `openWorld` option |\n * | `inputSchema` / `outputSchema` | the declared request and response types |\n *\n * ## Why the title is NOT an argument here (#1296)\n *\n * It used to be the second argument (#1180). That was a second place to write an endpoint's short\n * title: it never reached OpenAPI, whose operation `summary` was the bare method name, and the two\n * drifted. The title now has ONE source, the `@summary` JSDoc tag, which `wp-openapi` REQUIRES on every\n * published operation and publishes as OpenAPI `summary`, MCP `title` and MCP `annotations.title`.\n * Passing a title here is a compile error; the second parameter is the profiles object.\n *\n * Until #984 the decorator also took a `description`, duplicating the JSDoc, and was cut for the same\n * reason: two authored copies of one fact drift the first time somebody edits one.\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpMcpTool(name: string, options?: McpToolProfilesOptions): WpMcpMethodDecorator {\n if (\n options !== undefined &&\n (typeof options !== 'object' ||\n options === null ||\n Array.isArray(options) ||\n Object.keys(options).some((key: string) => key !== 'profiles'))\n ) {\n throw new Error(\n '@WpMcpTool options must be an object containing only profiles. The tool title is the ' +\n \"method's @summary JSDoc tag, never a decorator argument.\",\n );\n }\n const profiles = Mcp.profiles(\n options !== undefined && 'profiles' in options ? options.profiles! : [Mcp.DEFAULT],\n '@WpMcpTool',\n );\n const problem = McpToolName.problemWith(name);\n if (problem !== undefined) {\n throw new Error(`@WpMcpTool('${name}'): the tool name ${problem}. ${McpToolName.cure()}`);\n }\n return <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n _descriptor: ExactOneMethodDescriptor<TMethod>,\n ): void => {\n const apiClass = target.constructor;\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n const methodName = String(propertyKey);\n tools[methodName] = new WpMcpToolMetadata(methodName, name, profiles);\n Reflect.defineMetadata(METADATA_KEYS.MCP_TOOLS, tools, apiClass);\n };\n}\n\n/** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */\nexport class InvalidEndpointForMcpMetadata {\n constructor(\n public readonly methodName: string,\n /** WHY it can never be a tool. Non-empty, enforced at decoration time. */\n public readonly reason: string,\n ) {}\n}\n\n/**\n * Declares that this endpoint is PERMANENTLY outside MCP, and says why.\n *\n * ## It is not a suppression, and that distinction is the whole feature\n *\n * `// webpieces-disable api-rules-for-mcp -- <reason>` says \"stop complaining\". This says \"this is\n * structurally impossible, forever\", and the two have different futures: a suppression is something\n * somebody should eventually come back to, and this is something nobody should. Both measured cases\n * are types that are CORRECT and must not be \"fixed\" —\n *\n * - a webhook TRANSPORT envelope, whose `data` is owned by each event's own published contract;\n * typing it here would fork that ownership and make adding an event type a change to the\n * transport contract rather than a registration, and\n * - a freeform-SQL escape hatch, whose result columns depend on the model-authored `SELECT`.\n *\n * The second one is worth stating rather than burying: it is the most obviously useful tool in that\n * repo, and it is excluded by a type that is right.\n *\n * ## Absence already says \"not a tool YET\"\n *\n * A method with no `@WpMcpTool` is simply not published. This decorator is the NEVER, and the reason\n * is what makes `grep -rn InvalidEndpointForMcp` a list of permanent exclusions with their arguments\n * rather than a list of names. `api-rules-for-mcp` restates that list on every run, so an exclusion\n * stays visible instead of being announced once and forgotten.\n *\n * ## It contradicts `@WpMcpTool`\n *\n * Both on one method is an ERROR, asserted by {@link assertApiTypeMatchesMcpTools}. \"Publish this as\n * a tool\" and \"this can never be a tool\" are two spellings of one decision, which is exactly the\n * defect `.claude/rules/no-backwards-compat.md` shim shape #1 rejects.\n *\n * ```typescript\n * @InvalidEndpointForMcp('a transport envelope body is opaque by design; the published partner ' +\n * 'contract for each event type owns its shape')\n * ```\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function InvalidEndpointForMcp(reason: string): MethodDecorator {\n if (reason.trim() === '') {\n throw new Error(\n '@InvalidEndpointForMcp requires a non-empty reason. The reason IS the declaration — ' +\n 'without it this is a suppression with a longer name.',\n );\n }\n return (target: object, propertyKey: string | symbol): void => {\n const apiClass = target.constructor;\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n const methodName = String(propertyKey);\n excluded[methodName] = new InvalidEndpointForMcpMetadata(methodName, reason);\n Reflect.defineMetadata(METADATA_KEYS.MCP_INVALID, excluded, apiClass);\n };\n}\n\n/** Every method declared permanently outside MCP by {@link InvalidEndpointForMcp}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getInvalidEndpointsForMcp(apiClass: Function): InvalidEndpointForMcpMetadata[] {\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n return Object.values(excluded);\n}\n\n/** The one mapping from endpoint side effects to MCP advisory annotations. */\n// webpieces-disable no-function-outside-class -- pure metadata mapping shared by MCP registration\nexport function mcpHintsForOperation(\n operation: EndpointOperation,\n openWorldHint: boolean,\n): WpMcpToolHints {\n switch (operation) {\n case READ:\n return new WpMcpToolHints(true, false, true, openWorldHint);\n case WRITE_IDEMPOTENT:\n return new WpMcpToolHints(false, true, true, openWorldHint);\n case WRITE:\n return new WpMcpToolHints(false, true, false, openWorldHint);\n }\n}\n\n/** Returns only methods explicitly opted in through {@link WpMcpTool}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getWpMcpTools(apiClass: Function): WpMcpToolMetadata[] {\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n return Object.values(tools);\n}\n"]}
|
|
@@ -12,12 +12,16 @@ import { WpMcpToolHints } from './McpMetadata';
|
|
|
12
12
|
* which it is made of, are already here for the same reason.
|
|
13
13
|
*/
|
|
14
14
|
export declare class McpToolDefinition {
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The stable protocol name from `@WpMcpTool('<name>')` — 1-64 characters of `[A-Za-z0-9_-]`
|
|
17
|
+
* ({@link McpToolName}), refused on load otherwise.
|
|
18
|
+
*/
|
|
16
19
|
readonly name: string;
|
|
17
20
|
/**
|
|
18
|
-
* The human-readable display name
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
+
* The human-readable display name: the method's `@summary` JSDoc tag (#1296), the same line
|
|
22
|
+
* the OpenAPI operation publishes as its `summary`. REQUIRED (#1180): `tools/list` publishes it
|
|
23
|
+
* as both `Tool.title` and `annotations.title`, and a catalog entry without one is refused on
|
|
24
|
+
* load rather than silently publishing an untitled tool.
|
|
21
25
|
*/
|
|
22
26
|
readonly title: string;
|
|
23
27
|
/** The contract method it was rendered from, so a mismatch report can name the source. */
|
|
@@ -38,12 +42,16 @@ export declare class McpToolDefinition {
|
|
|
38
42
|
readonly outputSchema: ApiJsonSchema;
|
|
39
43
|
readonly profiles: readonly string[];
|
|
40
44
|
constructor(
|
|
41
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* The stable protocol name from `@WpMcpTool('<name>')` — 1-64 characters of `[A-Za-z0-9_-]`
|
|
47
|
+
* ({@link McpToolName}), refused on load otherwise.
|
|
48
|
+
*/
|
|
42
49
|
name: string,
|
|
43
50
|
/**
|
|
44
|
-
* The human-readable display name
|
|
45
|
-
*
|
|
46
|
-
*
|
|
51
|
+
* The human-readable display name: the method's `@summary` JSDoc tag (#1296), the same line
|
|
52
|
+
* the OpenAPI operation publishes as its `summary`. REQUIRED (#1180): `tools/list` publishes it
|
|
53
|
+
* as both `Tool.title` and `annotations.title`, and a catalog entry without one is refused on
|
|
54
|
+
* load rather than silently publishing an untitled tool.
|
|
47
55
|
*/
|
|
48
56
|
title: string,
|
|
49
57
|
/** The contract method it was rendered from, so a mismatch report can name the source. */
|
|
@@ -4,6 +4,7 @@ exports.McpToolCatalogFile = exports.McpToolCatalogError = exports.McpToolDefini
|
|
|
4
4
|
const DtoSchema_1 = require("./DtoSchema");
|
|
5
5
|
const Mcp_1 = require("./Mcp");
|
|
6
6
|
const McpMetadata_1 = require("./McpMetadata");
|
|
7
|
+
const McpToolName_1 = require("./McpToolName");
|
|
7
8
|
/**
|
|
8
9
|
* ONE generated MCP tool definition, including server-only profile membership.
|
|
9
10
|
*
|
|
@@ -25,12 +26,16 @@ class McpToolDefinition {
|
|
|
25
26
|
outputSchema;
|
|
26
27
|
profiles;
|
|
27
28
|
constructor(
|
|
28
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The stable protocol name from `@WpMcpTool('<name>')` — 1-64 characters of `[A-Za-z0-9_-]`
|
|
31
|
+
* ({@link McpToolName}), refused on load otherwise.
|
|
32
|
+
*/
|
|
29
33
|
name,
|
|
30
34
|
/**
|
|
31
|
-
* The human-readable display name
|
|
32
|
-
*
|
|
33
|
-
*
|
|
35
|
+
* The human-readable display name: the method's `@summary` JSDoc tag (#1296), the same line
|
|
36
|
+
* the OpenAPI operation publishes as its `summary`. REQUIRED (#1180): `tools/list` publishes it
|
|
37
|
+
* as both `Tool.title` and `annotations.title`, and a catalog entry without one is refused on
|
|
38
|
+
* load rather than silently publishing an untitled tool.
|
|
34
39
|
*/
|
|
35
40
|
title,
|
|
36
41
|
/** The contract method it was rendered from, so a mismatch report can name the source. */
|
|
@@ -109,10 +114,20 @@ class McpToolCatalogFile {
|
|
|
109
114
|
throw new McpToolCatalogError(`'${contractName}' is not a contract class name, so it cannot name an MCP tool catalog.`, REGENERATE);
|
|
110
115
|
}
|
|
111
116
|
const byName = new Map();
|
|
117
|
+
const badNames = [];
|
|
118
|
+
for (const tool of tools) {
|
|
119
|
+
const problem = McpToolName_1.McpToolName.problemWith(tool.name);
|
|
120
|
+
if (problem !== undefined)
|
|
121
|
+
badNames.push(`'${tool.name}' (${tool.methodName}) ${problem}`);
|
|
122
|
+
}
|
|
123
|
+
if (badNames.length > 0) {
|
|
124
|
+
throw new McpToolCatalogError(`The MCP tool catalog of ${contractName} carries ${badNames.length} illegal tool ` +
|
|
125
|
+
`name(s): ${badNames.join('; ')}.`, McpToolName_1.McpToolName.cure());
|
|
126
|
+
}
|
|
112
127
|
for (const tool of tools) {
|
|
113
128
|
if (byName.has(tool.name)) {
|
|
114
129
|
throw new McpToolCatalogError(`Duplicate MCP tool name '${tool.name}' in the generated catalog of ${contractName}.`, 'Tool names are the protocol identity and must be globally unique — rename one ' +
|
|
115
|
-
"of the two @WpMcpTool('<name>'
|
|
130
|
+
"of the two @WpMcpTool('<name>') declarations.");
|
|
116
131
|
}
|
|
117
132
|
byName.set(tool.name, tool);
|
|
118
133
|
}
|
|
@@ -267,8 +282,8 @@ class McpCatalogJson {
|
|
|
267
282
|
title(value, name) {
|
|
268
283
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
269
284
|
throw new McpToolCatalogError(`The MCP tool catalog has no 'title' on tool '${String(name)}'; every tool needs a ` +
|
|
270
|
-
"human-readable title from
|
|
271
|
-
'required titles (#1180).', REGENERATE);
|
|
285
|
+
"human-readable title, which the generator writes from the method's @summary JSDoc " +
|
|
286
|
+
'tag (#1296). The catalog predates required titles (#1180).', REGENERATE);
|
|
272
287
|
}
|
|
273
288
|
return value;
|
|
274
289
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpToolCatalogFile.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpToolCatalogFile.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaType } from './DtoSchema';\nimport { Mcp } from './Mcp';\nimport { WpMcpToolHints } from './McpMetadata';\n\n/**\n * ONE generated MCP tool definition, including server-only profile membership.\n *\n * It is BUILT from the source by `@webpieces/api-doc-model`'s `McpSchemaRenderer` at build time, and\n * READ here at boot — so the tool an agent is shown and the tool the server accepts are the same\n * bytes by construction, not by two declarations agreeing.\n *\n * It lives in `core-util` rather than in the extractor package because both ends need it and only\n * one end may depend on the TypeScript compiler API. `ApiJsonSchema` and {@link WpMcpToolHints},\n * which it is made of, are already here for the same reason.\n */\nexport class McpToolDefinition {\n constructor(\n /** The stable protocol name from `@WpMcpTool('<name>', '<title>')`. */\n readonly name: string,\n /**\n * The human-readable display name from `@WpMcpTool('<name>', '<title>')`. REQUIRED (#1180):\n * `tools/list` publishes it as both `Tool.title` and `annotations.title`, and a catalog entry\n * without one is refused on load rather than silently publishing an untitled tool.\n */\n readonly title: string,\n /** The contract method it was rendered from, so a mismatch report can name the source. */\n readonly methodName: string,\n /**\n * The tool documentation: the method's `@mcp` tag when it has one, its JSDoc body otherwise.\n * Documentation has exactly ONE source — the doc comment above the declaration — for the\n * partner reading OpenAPI and the agent reading `tools/list` alike.\n */\n readonly description: string,\n /**\n * All four hints. Three are COMPUTED from `@Endpoint`'s `operation` by\n * `mcpHintsForOperation`, which is the one place that mapping lives; `openWorldHint` comes\n * from `@Endpoint`'s `openWorld` option.\n */\n readonly hints: WpMcpToolHints,\n readonly inputSchema: ApiJsonSchema,\n readonly outputSchema: ApiJsonSchema,\n readonly profiles: readonly string[],\n ) {\n this.profiles = Mcp.profiles(profiles, 'MCP catalog tool');\n }\n}\n\n/** Thrown when a catalog is not one this release can read or write. */\nexport class McpToolCatalogError extends Error {\n constructor(\n message: string,\n /** What to do instead, in one sentence. */\n readonly cure: string,\n ) {\n super(`${message} ${cure}`);\n this.name = 'McpToolCatalogError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The one cure every catalog failure prescribes: rebuild the artifact from the contracts. */\nconst REGENERATE =\n 'Rebuild the api library that declares the contract (its `openapi-generate` target runs ' +\n '`wp-openapi`, which writes one `mcp-<ContractClass>-tools.json` per MCP contract).';\n\nconst FILE_PREFIX = 'mcp-';\nconst FILE_SUFFIX = '-tools.json';\n\n/** A class name, which is what the file name carries. Anything else is not one of these files. */\nconst CONTRACT_NAME = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * ONE contract's MCP tools, keyed by protocol name — the artifact `wp-openapi` writes as\n * `mcp-<ContractClass>-tools.json`, one file per contract that declares `MCP` and has at least one\n * `@WpMcpTool`, beside the library's OpenAPI documents.\n *\n * ## Why one file per CONTRACT and not one per library\n *\n * One api library holds many contracts (it is how a public partner API is grouped), and a server binds\n * contracts, not libraries — `McpApiBinding.local(LangCourseAuthorApi, ...)`. With one file per\n * contract the server can check every binding against exactly the file generated from THAT contract,\n * and refuse a file it was handed for a contract it does not bind. A single per-library file could only\n * say \"some tool is missing\" (#1021).\n *\n * This is the browser-safe CODEC — the bytes and the file name. Reading the files from an installed\n * package is `McpToolCatalog.fromPackages` in `@webpieces/mcp-server`, because only a node process\n * has a filesystem to read them from.\n */\nexport class McpToolCatalogFile {\n private readonly byName: ReadonlyMap<string, McpToolDefinition>;\n\n constructor(\n /** The contract CLASS name — `binding.api.name` at runtime, the class declaration at build time. */\n readonly contractName: string,\n readonly tools: readonly McpToolDefinition[],\n ) {\n if (!CONTRACT_NAME.test(contractName)) {\n throw new McpToolCatalogError(\n `'${contractName}' is not a contract class name, so it cannot name an MCP tool catalog.`,\n REGENERATE,\n );\n }\n const byName = new Map<string, McpToolDefinition>();\n for (const tool of tools) {\n if (byName.has(tool.name)) {\n throw new McpToolCatalogError(\n `Duplicate MCP tool name '${tool.name}' in the generated catalog of ${contractName}.`,\n 'Tool names are the protocol identity and must be globally unique — rename one ' +\n \"of the two @WpMcpTool('<name>', '<title>') declarations.\",\n );\n }\n byName.set(tool.name, tool);\n }\n this.byName = byName;\n }\n\n /** `mcp-<ContractClass>-tools.json`. */\n get fileName(): string {\n return McpToolCatalogFile.fileNameFor(this.contractName);\n }\n\n find(name: string): McpToolDefinition | undefined {\n return this.byName.get(name);\n }\n\n /** The names this catalog carries, sorted, so a failure can print what WAS published. */\n names(): readonly string[] {\n return [...this.byName.keys()].sort();\n }\n\n /** The bytes `wp-openapi` writes. Sorted by name so the artifact is diffable. */\n toJsonText(): string {\n const tools = [...this.tools].sort((left: McpToolDefinition, right: McpToolDefinition) =>\n left.name.localeCompare(right.name),\n );\n return `${JSON.stringify(tools, undefined, 2)}\\n`;\n }\n\n /** The file name the catalog of `contractName` is written to and read from. */\n // webpieces-disable no-function-outside-class -- static helper of this class\n static fileNameFor(contractName: string): string {\n return `${FILE_PREFIX}${contractName}${FILE_SUFFIX}`;\n }\n\n /** The contract a file name is the catalog of, or `undefined` when it is not one of these files. */\n // webpieces-disable no-function-outside-class -- static helper of this class\n static contractNameOf(fileName: string): string | undefined {\n if (!fileName.startsWith(FILE_PREFIX) || !fileName.endsWith(FILE_SUFFIX)) return undefined;\n const name = fileName.slice(FILE_PREFIX.length, fileName.length - FILE_SUFFIX.length);\n return CONTRACT_NAME.test(name) ? name : undefined;\n }\n\n /**\n * Reads one `mcp-<ContractClass>-tools.json`. The contract is the one the FILE NAME names. Every\n * nested value is rebuilt as a real class instance, because this is the one place untyped bytes\n * become the schemas a live protocol surface publishes.\n *\n * Malformed JSON is not caught here: it throws `SyntaxError` to the caller's own top-level\n * handler, which is where every other read-the-world failure in webpieces is reported.\n */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromJsonText(fileName: string, text: string): McpToolCatalogFile {\n const contractName = McpToolCatalogFile.contractNameOf(fileName);\n if (contractName === undefined) {\n throw new McpToolCatalogError(\n `'${fileName}' is not an MCP tool catalog file name; they are named ` +\n `${FILE_PREFIX}<ContractClass>${FILE_SUFFIX}.`,\n REGENERATE,\n );\n }\n return new McpToolCatalogFile(contractName, new McpCatalogJson().parse(text));\n }\n}\n\n/**\n * The PARSE BOUNDARY: untyped bytes in, real class instances out.\n *\n * A class of its own rather than statics on {@link McpToolCatalogFile} so every step is an ordinary\n * instance method — and because it is the one place in this file where `unknown` is the correct type\n * rather than a missing one. Nothing else in webpieces should ever hold a half-narrowed schema.\n */\nclass McpCatalogJson {\n parse(text: string): readonly McpToolDefinition[] {\n // webpieces-disable no-any-unknown -- this IS the untyped-bytes boundary; every step below narrows it\n const parsed: unknown = JSON.parse(text);\n if (!Array.isArray(parsed)) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog must be a JSON array of tool definitions.',\n REGENERATE,\n );\n }\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n return parsed.map((entry: unknown) => this.toolFrom(entry));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private toolFrom(entry: unknown): McpToolDefinition {\n const record = this.record(entry, 'a tool definition');\n const hints = this.record(record['hints'], 'a tool definition hints block');\n return new McpToolDefinition(\n this.text(record['name'], 'name'),\n this.title(record['title'], record['name']),\n this.text(record['methodName'], 'methodName'),\n this.text(record['description'], 'description'),\n new WpMcpToolHints(\n hints['readOnlyHint'] === true,\n hints['destructiveHint'] === true,\n hints['idempotentHint'] === true,\n hints['openWorldHint'] === true,\n ),\n this.schemaFrom(record['inputSchema']),\n this.schemaFrom(record['outputSchema']),\n this.profiles(record['profiles']),\n );\n }\n\n // webpieces-disable no-any-unknown -- JSON catalog parse boundary\n private profiles(value: unknown): readonly string[] {\n if (!Array.isArray(value) || !value.every((entry: unknown) => typeof entry === 'string')) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog requires profiles (#1187); stale artifacts are refused.',\n REGENERATE,\n );\n }\n return Mcp.profiles(value, 'MCP catalog profiles',\n (message: string) => new McpToolCatalogError(message, REGENERATE));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private schemaFrom(value: unknown): ApiJsonSchema {\n const record = this.record(value, 'a JSON schema');\n const schema = new ApiJsonSchema(\n record['type'] as ApiJsonSchemaType | readonly ApiJsonSchemaType[],\n );\n if (typeof record['description'] === 'string') schema.description = record['description'];\n if (typeof record['minimum'] === 'number') schema.minimum = record['minimum'];\n if (typeof record['maximum'] === 'number') schema.maximum = record['maximum'];\n if (Array.isArray(record['required'])) schema.required = record['required'] as string[];\n if (Array.isArray(record['enum'])) schema.enum = record['enum'] as (string | null)[];\n if (typeof record['x-mcp-header'] === 'string') {\n schema['x-mcp-header'] = record['x-mcp-header'];\n }\n if (record['items'] !== undefined) schema.items = this.schemaFrom(record['items']);\n const extra = record['additionalProperties'];\n if (typeof extra === 'boolean') schema.additionalProperties = extra;\n else if (extra !== undefined) schema.additionalProperties = this.schemaFrom(extra);\n const properties = record['properties'];\n if (properties !== undefined) {\n const built: Record<string, ApiJsonSchema> = {};\n for (const [key, child] of Object.entries(\n this.record(properties, 'a schema properties block'),\n )) {\n built[key] = this.schemaFrom(child);\n }\n schema.properties = built;\n }\n const oneOf = record['oneOf'];\n if (Array.isArray(oneOf)) {\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n schema.oneOf = oneOf.map((branch: unknown) => this.schemaFrom(branch));\n schema.discriminator = this.discriminatorFrom(record['discriminator']);\n }\n return schema;\n }\n\n /**\n * The `discriminator` block, or undefined — a union TypeScript cannot narrow is published WITHOUT\n * one, so an absent block is a legal document and not a missing field.\n */\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private discriminatorFrom(value: unknown): ApiJsonSchemaDiscriminator | undefined {\n if (value === undefined) return undefined;\n const record = this.record(value, 'a schema discriminator block');\n const mapping: Record<string, string> = {};\n for (const [key, branch] of Object.entries(\n this.record(record['mapping'], 'a discriminator mapping block'),\n )) {\n mapping[key] = this.text(branch, `discriminator mapping '${key}'`);\n }\n return new ApiJsonSchemaDiscriminator(\n this.text(record['propertyName'], 'discriminator propertyName'),\n mapping,\n );\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private record(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new McpToolCatalogError(`The MCP tool catalog is missing ${what}.`, REGENERATE);\n }\n // webpieces-disable no-any-unknown -- the narrowed result of the check above\n return value as Record<string, unknown>;\n }\n\n /**\n * The REQUIRED, non-blank `title` (#1180). A catalog generated before titles existed has none, and\n * is refused here so a stale artifact fails the boot loudly instead of publishing untitled tools.\n */\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private title(value: unknown, name: unknown): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no 'title' on tool '${String(name)}'; every tool needs a ` +\n \"human-readable title from @WpMcpTool('<name>', '<title>'). The catalog predates \" +\n 'required titles (#1180).',\n REGENERATE,\n );\n }\n return value;\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private text(value: unknown, field: string): string {\n if (typeof value !== 'string') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no '${field}' on a tool definition.`,\n REGENERATE,\n );\n }\n return value;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"McpToolCatalogFile.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpToolCatalogFile.ts"],"names":[],"mappings":";;;AAAA,2CAA2F;AAC3F,+BAA4B;AAC5B,+CAA+C;AAC/C,+CAA4C;AAE5C;;;;;;;;;;GAUG;AACH,MAAa,iBAAiB;IAMb;IAOA;IAEA;IAMA;IAMA;IACA;IACA;IACA;IA7Bb;IACI;;;OAGG;IACM,IAAY;IACrB;;;;;OAKG;IACM,KAAa;IACtB,0FAA0F;IACjF,UAAkB;IAC3B;;;;OAIG;IACM,WAAmB;IAC5B;;;;OAIG;IACM,KAAqB,EACrB,WAA0B,EAC1B,YAA2B,EAC3B,QAA2B;QAxB3B,SAAI,GAAJ,IAAI,CAAQ;QAOZ,UAAK,GAAL,KAAK,CAAQ;QAEb,eAAU,GAAV,UAAU,CAAQ;QAMlB,gBAAW,GAAX,WAAW,CAAQ;QAMnB,UAAK,GAAL,KAAK,CAAgB;QACrB,gBAAW,GAAX,WAAW,CAAe;QAC1B,iBAAY,GAAZ,YAAY,CAAe;QAC3B,aAAQ,GAAR,QAAQ,CAAmB;QAEpC,IAAI,CAAC,QAAQ,GAAG,SAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAC/D,CAAC;CACJ;AAlCD,8CAkCC;AAED,uEAAuE;AACvE,MAAa,mBAAoB,SAAQ,KAAK;IAI7B;IAHb,YACI,OAAe;IACf,2CAA2C;IAClC,IAAY;QAErB,KAAK,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC;QAFnB,SAAI,GAAJ,IAAI,CAAQ;QAGrB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACJ;AAVD,kDAUC;AAED,8FAA8F;AAC9F,MAAM,UAAU,GACZ,yFAAyF;IACzF,oFAAoF,CAAC;AAEzF,MAAM,WAAW,GAAG,MAAM,CAAC;AAC3B,MAAM,WAAW,GAAG,aAAa,CAAC;AAElC,kGAAkG;AAClG,MAAM,aAAa,GAAG,4BAA4B,CAAC;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,kBAAkB;IAKd;IACA;IALI,MAAM,CAAyC;IAEhE;IACI,oGAAoG;IAC3F,YAAoB,EACpB,KAAmC;QADnC,iBAAY,GAAZ,YAAY,CAAQ;QACpB,UAAK,GAAL,KAAK,CAA8B;QAE5C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,mBAAmB,CACzB,IAAI,YAAY,wEAAwE,EACxF,UAAU,CACb,CAAC;QACN,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;QACpD,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,yBAAW,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,OAAO,KAAK,SAAS;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,mBAAmB,CACzB,2BAA2B,YAAY,YAAY,QAAQ,CAAC,MAAM,gBAAgB;gBAC9E,YAAY,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EACtC,yBAAW,CAAC,IAAI,EAAE,CACrB,CAAC;QACN,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,mBAAmB,CACzB,4BAA4B,IAAI,CAAC,IAAI,iCAAiC,YAAY,GAAG,EACrF,gFAAgF;oBAC5E,+CAA+C,CACtD,CAAC;YACN,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,wCAAwC;IACxC,IAAI,QAAQ;QACR,OAAO,kBAAkB,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,CAAC,IAAY;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,yFAAyF;IACzF,KAAK;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;IAED,iFAAiF;IACjF,UAAU;QACN,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAuB,EAAE,KAAwB,EAAE,EAAE,CACrF,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CACtC,CAAC;QACF,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAED,+EAA+E;IAC/E,6EAA6E;IAC7E,MAAM,CAAC,WAAW,CAAC,YAAoB;QACnC,OAAO,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,EAAE,CAAC;IACzD,CAAC;IAED,oGAAoG;IACpG,6EAA6E;IAC7E,MAAM,CAAC,cAAc,CAAC,QAAgB;QAClC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3F,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACtF,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,CAAC;IAED;;;;;;;OAOG;IACH,8EAA8E;IAC9E,MAAM,CAAC,YAAY,CAAC,QAAgB,EAAE,IAAY;QAC9C,MAAM,YAAY,GAAG,kBAAkB,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACjE,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,mBAAmB,CACzB,IAAI,QAAQ,yDAAyD;gBACjE,GAAG,WAAW,kBAAkB,WAAW,GAAG,EAClD,UAAU,CACb,CAAC;QACN,CAAC;QACD,OAAO,IAAI,kBAAkB,CAAC,YAAY,EAAE,IAAI,cAAc,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAClF,CAAC;CACJ;AAhGD,gDAgGC;AAED;;;;;;GAMG;AACH,MAAM,cAAc;IAChB,KAAK,CAAC,IAAY;QACd,sGAAsG;QACtG,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,mBAAmB,CACzB,gEAAgE,EAChE,UAAU,CACb,CAAC;QACN,CAAC;QACD,oFAAoF;QACpF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,oFAAoF;IAC5E,QAAQ,CAAC,KAAc;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,+BAA+B,CAAC,CAAC;QAC5E,OAAO,IAAI,iBAAiB,CACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,EAC7C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,EAC/C,IAAI,4BAAc,CACd,KAAK,CAAC,cAAc,CAAC,KAAK,IAAI,EAC9B,KAAK,CAAC,iBAAiB,CAAC,KAAK,IAAI,EACjC,KAAK,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAChC,KAAK,CAAC,eAAe,CAAC,KAAK,IAAI,CAClC,EACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EACtC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,EACvC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CACpC,CAAC;IACN,CAAC;IAED,kEAAkE;IAC1D,QAAQ,CAAC,KAAc;QAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,mBAAmB,CACzB,8EAA8E,EAC9E,UAAU,CACb,CAAC;QACN,CAAC;QACD,OAAO,SAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,sBAAsB,EAC7C,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,oFAAoF;IAC5E,UAAU,CAAC,KAAc;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,yBAAa,CAC5B,MAAM,CAAC,MAAM,CAAqD,CACrE,CAAC;QACF,IAAI,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,QAAQ;YAAE,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QAC1F,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,QAAQ;YAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9E,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,QAAQ;YAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAa,CAAC;QACxF,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAsB,CAAC;QACrF,IAAI,OAAO,MAAM,CAAC,cAAc,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC7C,MAAM,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS;YAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC7C,IAAI,OAAO,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,oBAAoB,GAAG,KAAK,CAAC;aAC/D,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACnF,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QACxC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAkC,EAAE,CAAC;YAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CACrC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,2BAA2B,CAAC,CACvD,EAAE,CAAC;gBACA,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;QAC9B,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,oFAAoF;YACpF,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,MAAe,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;OAGG;IACH,oFAAoF;IAC5E,iBAAiB,CAAC,KAAc;QACpC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,8BAA8B,CAAC,CAAC;QAClE,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CACtC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,+BAA+B,CAAC,CAClE,EAAE,CAAC;YACA,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,GAAG,GAAG,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,IAAI,sCAA0B,CACjC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,4BAA4B,CAAC,EAC/D,OAAO,CACV,CAAC;IACN,CAAC;IAED,oFAAoF;IAC5E,MAAM,CAAC,KAAc,EAAE,IAAY;QACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,mBAAmB,CAAC,mCAAmC,IAAI,GAAG,EAAE,UAAU,CAAC,CAAC;QAC1F,CAAC;QACD,6EAA6E;QAC7E,OAAO,KAAgC,CAAC;IAC5C,CAAC;IAED;;;OAGG;IACH,oFAAoF;IAC5E,KAAK,CAAC,KAAc,EAAE,IAAa;QACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACnD,MAAM,IAAI,mBAAmB,CACzB,gDAAgD,MAAM,CAAC,IAAI,CAAC,wBAAwB;gBAChF,oFAAoF;gBACpF,4DAA4D,EAChE,UAAU,CACb,CAAC;QACN,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,oFAAoF;IAC5E,IAAI,CAAC,KAAc,EAAE,KAAa;QACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,IAAI,mBAAmB,CACzB,gCAAgC,KAAK,yBAAyB,EAC9D,UAAU,CACb,CAAC;QACN,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ","sourcesContent":["import { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaType } from './DtoSchema';\nimport { Mcp } from './Mcp';\nimport { WpMcpToolHints } from './McpMetadata';\nimport { McpToolName } from './McpToolName';\n\n/**\n * ONE generated MCP tool definition, including server-only profile membership.\n *\n * It is BUILT from the source by `@webpieces/api-doc-model`'s `McpSchemaRenderer` at build time, and\n * READ here at boot — so the tool an agent is shown and the tool the server accepts are the same\n * bytes by construction, not by two declarations agreeing.\n *\n * It lives in `core-util` rather than in the extractor package because both ends need it and only\n * one end may depend on the TypeScript compiler API. `ApiJsonSchema` and {@link WpMcpToolHints},\n * which it is made of, are already here for the same reason.\n */\nexport class McpToolDefinition {\n constructor(\n /**\n * The stable protocol name from `@WpMcpTool('<name>')` — 1-64 characters of `[A-Za-z0-9_-]`\n * ({@link McpToolName}), refused on load otherwise.\n */\n readonly name: string,\n /**\n * The human-readable display name: the method's `@summary` JSDoc tag (#1296), the same line\n * the OpenAPI operation publishes as its `summary`. REQUIRED (#1180): `tools/list` publishes it\n * as both `Tool.title` and `annotations.title`, and a catalog entry without one is refused on\n * load rather than silently publishing an untitled tool.\n */\n readonly title: string,\n /** The contract method it was rendered from, so a mismatch report can name the source. */\n readonly methodName: string,\n /**\n * The tool documentation: the method's `@mcp` tag when it has one, its JSDoc body otherwise.\n * Documentation has exactly ONE source — the doc comment above the declaration — for the\n * partner reading OpenAPI and the agent reading `tools/list` alike.\n */\n readonly description: string,\n /**\n * All four hints. Three are COMPUTED from `@Endpoint`'s `operation` by\n * `mcpHintsForOperation`, which is the one place that mapping lives; `openWorldHint` comes\n * from `@Endpoint`'s `openWorld` option.\n */\n readonly hints: WpMcpToolHints,\n readonly inputSchema: ApiJsonSchema,\n readonly outputSchema: ApiJsonSchema,\n readonly profiles: readonly string[],\n ) {\n this.profiles = Mcp.profiles(profiles, 'MCP catalog tool');\n }\n}\n\n/** Thrown when a catalog is not one this release can read or write. */\nexport class McpToolCatalogError extends Error {\n constructor(\n message: string,\n /** What to do instead, in one sentence. */\n readonly cure: string,\n ) {\n super(`${message} ${cure}`);\n this.name = 'McpToolCatalogError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The one cure every catalog failure prescribes: rebuild the artifact from the contracts. */\nconst REGENERATE =\n 'Rebuild the api library that declares the contract (its `openapi-generate` target runs ' +\n '`wp-openapi`, which writes one `mcp-<ContractClass>-tools.json` per MCP contract).';\n\nconst FILE_PREFIX = 'mcp-';\nconst FILE_SUFFIX = '-tools.json';\n\n/** A class name, which is what the file name carries. Anything else is not one of these files. */\nconst CONTRACT_NAME = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * ONE contract's MCP tools, keyed by protocol name — the artifact `wp-openapi` writes as\n * `mcp-<ContractClass>-tools.json`, one file per contract that declares `MCP` and has at least one\n * `@WpMcpTool`, beside the library's OpenAPI documents.\n *\n * ## Why one file per CONTRACT and not one per library\n *\n * One api library holds many contracts (it is how a public partner API is grouped), and a server binds\n * contracts, not libraries — `McpApiBinding.local(LangCourseAuthorApi, ...)`. With one file per\n * contract the server can check every binding against exactly the file generated from THAT contract,\n * and refuse a file it was handed for a contract it does not bind. A single per-library file could only\n * say \"some tool is missing\" (#1021).\n *\n * This is the browser-safe CODEC — the bytes and the file name. Reading the files from an installed\n * package is `McpToolCatalog.fromPackages` in `@webpieces/mcp-server`, because only a node process\n * has a filesystem to read them from.\n */\nexport class McpToolCatalogFile {\n private readonly byName: ReadonlyMap<string, McpToolDefinition>;\n\n constructor(\n /** The contract CLASS name — `binding.api.name` at runtime, the class declaration at build time. */\n readonly contractName: string,\n readonly tools: readonly McpToolDefinition[],\n ) {\n if (!CONTRACT_NAME.test(contractName)) {\n throw new McpToolCatalogError(\n `'${contractName}' is not a contract class name, so it cannot name an MCP tool catalog.`,\n REGENERATE,\n );\n }\n const byName = new Map<string, McpToolDefinition>();\n const badNames: string[] = [];\n for (const tool of tools) {\n const problem = McpToolName.problemWith(tool.name);\n if (problem !== undefined) badNames.push(`'${tool.name}' (${tool.methodName}) ${problem}`);\n }\n if (badNames.length > 0) {\n throw new McpToolCatalogError(\n `The MCP tool catalog of ${contractName} carries ${badNames.length} illegal tool ` +\n `name(s): ${badNames.join('; ')}.`,\n McpToolName.cure(),\n );\n }\n for (const tool of tools) {\n if (byName.has(tool.name)) {\n throw new McpToolCatalogError(\n `Duplicate MCP tool name '${tool.name}' in the generated catalog of ${contractName}.`,\n 'Tool names are the protocol identity and must be globally unique — rename one ' +\n \"of the two @WpMcpTool('<name>') declarations.\",\n );\n }\n byName.set(tool.name, tool);\n }\n this.byName = byName;\n }\n\n /** `mcp-<ContractClass>-tools.json`. */\n get fileName(): string {\n return McpToolCatalogFile.fileNameFor(this.contractName);\n }\n\n find(name: string): McpToolDefinition | undefined {\n return this.byName.get(name);\n }\n\n /** The names this catalog carries, sorted, so a failure can print what WAS published. */\n names(): readonly string[] {\n return [...this.byName.keys()].sort();\n }\n\n /** The bytes `wp-openapi` writes. Sorted by name so the artifact is diffable. */\n toJsonText(): string {\n const tools = [...this.tools].sort((left: McpToolDefinition, right: McpToolDefinition) =>\n left.name.localeCompare(right.name),\n );\n return `${JSON.stringify(tools, undefined, 2)}\\n`;\n }\n\n /** The file name the catalog of `contractName` is written to and read from. */\n // webpieces-disable no-function-outside-class -- static helper of this class\n static fileNameFor(contractName: string): string {\n return `${FILE_PREFIX}${contractName}${FILE_SUFFIX}`;\n }\n\n /** The contract a file name is the catalog of, or `undefined` when it is not one of these files. */\n // webpieces-disable no-function-outside-class -- static helper of this class\n static contractNameOf(fileName: string): string | undefined {\n if (!fileName.startsWith(FILE_PREFIX) || !fileName.endsWith(FILE_SUFFIX)) return undefined;\n const name = fileName.slice(FILE_PREFIX.length, fileName.length - FILE_SUFFIX.length);\n return CONTRACT_NAME.test(name) ? name : undefined;\n }\n\n /**\n * Reads one `mcp-<ContractClass>-tools.json`. The contract is the one the FILE NAME names. Every\n * nested value is rebuilt as a real class instance, because this is the one place untyped bytes\n * become the schemas a live protocol surface publishes.\n *\n * Malformed JSON is not caught here: it throws `SyntaxError` to the caller's own top-level\n * handler, which is where every other read-the-world failure in webpieces is reported.\n */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromJsonText(fileName: string, text: string): McpToolCatalogFile {\n const contractName = McpToolCatalogFile.contractNameOf(fileName);\n if (contractName === undefined) {\n throw new McpToolCatalogError(\n `'${fileName}' is not an MCP tool catalog file name; they are named ` +\n `${FILE_PREFIX}<ContractClass>${FILE_SUFFIX}.`,\n REGENERATE,\n );\n }\n return new McpToolCatalogFile(contractName, new McpCatalogJson().parse(text));\n }\n}\n\n/**\n * The PARSE BOUNDARY: untyped bytes in, real class instances out.\n *\n * A class of its own rather than statics on {@link McpToolCatalogFile} so every step is an ordinary\n * instance method — and because it is the one place in this file where `unknown` is the correct type\n * rather than a missing one. Nothing else in webpieces should ever hold a half-narrowed schema.\n */\nclass McpCatalogJson {\n parse(text: string): readonly McpToolDefinition[] {\n // webpieces-disable no-any-unknown -- this IS the untyped-bytes boundary; every step below narrows it\n const parsed: unknown = JSON.parse(text);\n if (!Array.isArray(parsed)) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog must be a JSON array of tool definitions.',\n REGENERATE,\n );\n }\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n return parsed.map((entry: unknown) => this.toolFrom(entry));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private toolFrom(entry: unknown): McpToolDefinition {\n const record = this.record(entry, 'a tool definition');\n const hints = this.record(record['hints'], 'a tool definition hints block');\n return new McpToolDefinition(\n this.text(record['name'], 'name'),\n this.title(record['title'], record['name']),\n this.text(record['methodName'], 'methodName'),\n this.text(record['description'], 'description'),\n new WpMcpToolHints(\n hints['readOnlyHint'] === true,\n hints['destructiveHint'] === true,\n hints['idempotentHint'] === true,\n hints['openWorldHint'] === true,\n ),\n this.schemaFrom(record['inputSchema']),\n this.schemaFrom(record['outputSchema']),\n this.profiles(record['profiles']),\n );\n }\n\n // webpieces-disable no-any-unknown -- JSON catalog parse boundary\n private profiles(value: unknown): readonly string[] {\n if (!Array.isArray(value) || !value.every((entry: unknown) => typeof entry === 'string')) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog requires profiles (#1187); stale artifacts are refused.',\n REGENERATE,\n );\n }\n return Mcp.profiles(value, 'MCP catalog profiles',\n (message: string) => new McpToolCatalogError(message, REGENERATE));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private schemaFrom(value: unknown): ApiJsonSchema {\n const record = this.record(value, 'a JSON schema');\n const schema = new ApiJsonSchema(\n record['type'] as ApiJsonSchemaType | readonly ApiJsonSchemaType[],\n );\n if (typeof record['description'] === 'string') schema.description = record['description'];\n if (typeof record['minimum'] === 'number') schema.minimum = record['minimum'];\n if (typeof record['maximum'] === 'number') schema.maximum = record['maximum'];\n if (Array.isArray(record['required'])) schema.required = record['required'] as string[];\n if (Array.isArray(record['enum'])) schema.enum = record['enum'] as (string | null)[];\n if (typeof record['x-mcp-header'] === 'string') {\n schema['x-mcp-header'] = record['x-mcp-header'];\n }\n if (record['items'] !== undefined) schema.items = this.schemaFrom(record['items']);\n const extra = record['additionalProperties'];\n if (typeof extra === 'boolean') schema.additionalProperties = extra;\n else if (extra !== undefined) schema.additionalProperties = this.schemaFrom(extra);\n const properties = record['properties'];\n if (properties !== undefined) {\n const built: Record<string, ApiJsonSchema> = {};\n for (const [key, child] of Object.entries(\n this.record(properties, 'a schema properties block'),\n )) {\n built[key] = this.schemaFrom(child);\n }\n schema.properties = built;\n }\n const oneOf = record['oneOf'];\n if (Array.isArray(oneOf)) {\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n schema.oneOf = oneOf.map((branch: unknown) => this.schemaFrom(branch));\n schema.discriminator = this.discriminatorFrom(record['discriminator']);\n }\n return schema;\n }\n\n /**\n * The `discriminator` block, or undefined — a union TypeScript cannot narrow is published WITHOUT\n * one, so an absent block is a legal document and not a missing field.\n */\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private discriminatorFrom(value: unknown): ApiJsonSchemaDiscriminator | undefined {\n if (value === undefined) return undefined;\n const record = this.record(value, 'a schema discriminator block');\n const mapping: Record<string, string> = {};\n for (const [key, branch] of Object.entries(\n this.record(record['mapping'], 'a discriminator mapping block'),\n )) {\n mapping[key] = this.text(branch, `discriminator mapping '${key}'`);\n }\n return new ApiJsonSchemaDiscriminator(\n this.text(record['propertyName'], 'discriminator propertyName'),\n mapping,\n );\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private record(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new McpToolCatalogError(`The MCP tool catalog is missing ${what}.`, REGENERATE);\n }\n // webpieces-disable no-any-unknown -- the narrowed result of the check above\n return value as Record<string, unknown>;\n }\n\n /**\n * The REQUIRED, non-blank `title` (#1180). A catalog generated before titles existed has none, and\n * is refused here so a stale artifact fails the boot loudly instead of publishing untitled tools.\n */\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private title(value: unknown, name: unknown): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no 'title' on tool '${String(name)}'; every tool needs a ` +\n \"human-readable title, which the generator writes from the method's @summary JSDoc \" +\n 'tag (#1296). The catalog predates required titles (#1180).',\n REGENERATE,\n );\n }\n return value;\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private text(value: unknown, field: string): string {\n if (typeof value !== 'string') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no '${field}' on a tool definition.`,\n REGENERATE,\n );\n }\n return value;\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE rule for an MCP tool's stable protocol `name` (#1296): 1–64 characters of `[A-Za-z0-9_-]`.
|
|
3
|
+
*
|
|
4
|
+
* MCP 2025-11-25 says a name SHOULD be 1–128 characters of `[A-Za-z0-9_.-]`, but the Claude
|
|
5
|
+
* Connectors Directory requires 64 or fewer, so a server publishing a longer one cannot be listed.
|
|
6
|
+
* The dot is left out as well: a directory-safe name is one every client accepts.
|
|
7
|
+
*
|
|
8
|
+
* It lives here, beside the decorator and the catalog codec, so every place that judges a name asks
|
|
9
|
+
* the SAME question: `@WpMcpTool` at decoration time, `wp-openapi` before it writes a catalog (naming
|
|
10
|
+
* every offender in one run), `McpToolCatalogFile` when a server boots from one, and
|
|
11
|
+
* `api-rules-for-mcp`. Four copies of a regex would be four chances to disagree.
|
|
12
|
+
*/
|
|
13
|
+
export declare class McpToolName {
|
|
14
|
+
/** The Claude Connectors Directory's limit. */
|
|
15
|
+
static readonly MAX_LENGTH = 64;
|
|
16
|
+
/** The characters a directory-safe tool name may use. */
|
|
17
|
+
static readonly CHARSET: RegExp;
|
|
18
|
+
/** What is wrong with `name`, in one clause, or undefined when it is a legal tool name. */
|
|
19
|
+
static problemWith(name: string): string | undefined;
|
|
20
|
+
/** The one cure every refusal of a tool name prints. */
|
|
21
|
+
static cure(): string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpToolName = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* THE rule for an MCP tool's stable protocol `name` (#1296): 1–64 characters of `[A-Za-z0-9_-]`.
|
|
6
|
+
*
|
|
7
|
+
* MCP 2025-11-25 says a name SHOULD be 1–128 characters of `[A-Za-z0-9_.-]`, but the Claude
|
|
8
|
+
* Connectors Directory requires 64 or fewer, so a server publishing a longer one cannot be listed.
|
|
9
|
+
* The dot is left out as well: a directory-safe name is one every client accepts.
|
|
10
|
+
*
|
|
11
|
+
* It lives here, beside the decorator and the catalog codec, so every place that judges a name asks
|
|
12
|
+
* the SAME question: `@WpMcpTool` at decoration time, `wp-openapi` before it writes a catalog (naming
|
|
13
|
+
* every offender in one run), `McpToolCatalogFile` when a server boots from one, and
|
|
14
|
+
* `api-rules-for-mcp`. Four copies of a regex would be four chances to disagree.
|
|
15
|
+
*/
|
|
16
|
+
class McpToolName {
|
|
17
|
+
/** The Claude Connectors Directory's limit. */
|
|
18
|
+
static MAX_LENGTH = 64;
|
|
19
|
+
/** The characters a directory-safe tool name may use. */
|
|
20
|
+
static CHARSET = /^[A-Za-z0-9_-]+$/;
|
|
21
|
+
/** What is wrong with `name`, in one clause, or undefined when it is a legal tool name. */
|
|
22
|
+
// webpieces-disable no-function-outside-class -- static helper of this class; the rule has no state
|
|
23
|
+
static problemWith(name) {
|
|
24
|
+
if (name.length === 0) {
|
|
25
|
+
return 'is empty';
|
|
26
|
+
}
|
|
27
|
+
if (name.length > McpToolName.MAX_LENGTH) {
|
|
28
|
+
return `is ${name.length} characters, over the ${McpToolName.MAX_LENGTH}-character limit`;
|
|
29
|
+
}
|
|
30
|
+
if (!McpToolName.CHARSET.test(name)) {
|
|
31
|
+
return 'uses a character outside A-Z a-z 0-9 _ -';
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/** The one cure every refusal of a tool name prints. */
|
|
36
|
+
// webpieces-disable no-function-outside-class -- static helper of this class; the rule has no state
|
|
37
|
+
static cure() {
|
|
38
|
+
return (`Rename it in @WpMcpTool('<name>'): 1-${McpToolName.MAX_LENGTH} characters of ` +
|
|
39
|
+
'A-Z a-z 0-9 _ -. The Claude Connectors Directory refuses a server whose tool names are ' +
|
|
40
|
+
`longer than ${McpToolName.MAX_LENGTH}. The name is the protocol identity agents save, so ` +
|
|
41
|
+
'pick the final one now.');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.McpToolName = McpToolName;
|
|
45
|
+
//# sourceMappingURL=McpToolName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpToolName.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpToolName.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,MAAa,WAAW;IACpB,+CAA+C;IAC/C,MAAM,CAAU,UAAU,GAAG,EAAE,CAAC;IAEhC,yDAAyD;IACzD,MAAM,CAAU,OAAO,GAAG,kBAAkB,CAAC;IAE7C,2FAA2F;IAC3F,oGAAoG;IACpG,MAAM,CAAC,WAAW,CAAC,IAAY;QAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,UAAU,CAAC;QACtB,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YACvC,OAAO,MAAM,IAAI,CAAC,MAAM,yBAAyB,WAAW,CAAC,UAAU,kBAAkB,CAAC;QAC9F,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,OAAO,0CAA0C,CAAC;QACtD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,wDAAwD;IACxD,oGAAoG;IACpG,MAAM,CAAC,IAAI;QACP,OAAO,CACH,wCAAwC,WAAW,CAAC,UAAU,iBAAiB;YAC/E,yFAAyF;YACzF,eAAe,WAAW,CAAC,UAAU,sDAAsD;YAC3F,yBAAyB,CAC5B,CAAC;IACN,CAAC;;AA/BL,kCAgCC","sourcesContent":["/**\n * THE rule for an MCP tool's stable protocol `name` (#1296): 1–64 characters of `[A-Za-z0-9_-]`.\n *\n * MCP 2025-11-25 says a name SHOULD be 1–128 characters of `[A-Za-z0-9_.-]`, but the Claude\n * Connectors Directory requires 64 or fewer, so a server publishing a longer one cannot be listed.\n * The dot is left out as well: a directory-safe name is one every client accepts.\n *\n * It lives here, beside the decorator and the catalog codec, so every place that judges a name asks\n * the SAME question: `@WpMcpTool` at decoration time, `wp-openapi` before it writes a catalog (naming\n * every offender in one run), `McpToolCatalogFile` when a server boots from one, and\n * `api-rules-for-mcp`. Four copies of a regex would be four chances to disagree.\n */\nexport class McpToolName {\n /** The Claude Connectors Directory's limit. */\n static readonly MAX_LENGTH = 64;\n\n /** The characters a directory-safe tool name may use. */\n static readonly CHARSET = /^[A-Za-z0-9_-]+$/;\n\n /** What is wrong with `name`, in one clause, or undefined when it is a legal tool name. */\n // webpieces-disable no-function-outside-class -- static helper of this class; the rule has no state\n static problemWith(name: string): string | undefined {\n if (name.length === 0) {\n return 'is empty';\n }\n if (name.length > McpToolName.MAX_LENGTH) {\n return `is ${name.length} characters, over the ${McpToolName.MAX_LENGTH}-character limit`;\n }\n if (!McpToolName.CHARSET.test(name)) {\n return 'uses a character outside A-Z a-z 0-9 _ -';\n }\n return undefined;\n }\n\n /** The one cure every refusal of a tool name prints. */\n // webpieces-disable no-function-outside-class -- static helper of this class; the rule has no state\n static cure(): string {\n return (\n `Rename it in @WpMcpTool('<name>'): 1-${McpToolName.MAX_LENGTH} characters of ` +\n 'A-Z a-z 0-9 _ -. The Claude Connectors Directory refuses a server whose tool names are ' +\n `longer than ${McpToolName.MAX_LENGTH}. The name is the protocol identity agents save, so ` +\n 'pick the final one now.'\n );\n }\n}\n"]}
|