diff --git a/.gitignore b/.gitignore index 6c4bf1a6..b39dd94e 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,9 @@ web_modules/ # Output of 'npm pack' *.tgz +# Output of 'npm run fetch:spec-types' +src/spec.types.ts + # Yarn Integrity file .yarn-integrity diff --git a/package.json b/package.json index 15b7753b..bbca197b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dist" ], "scripts": { + "fetch:spec-types": "test -f src/spec.types.ts || curl -o src/spec.types.ts https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/heads/main/schema/draft/schema.ts", "build": "npm run build:esm && npm run build:cjs", "build:esm": "mkdir -p dist/esm && echo '{\"type\": \"module\"}' > dist/esm/package.json && tsc -p tsconfig.prod.json", "build:esm:w": "npm run build:esm -- -w", @@ -43,7 +44,7 @@ "examples:simple-server:w": "tsx --watch src/examples/server/simpleStreamableHttp.ts --oauth", "prepack": "npm run build:esm && npm run build:cjs", "lint": "eslint src/", - "test": "jest", + "test": "npm run fetch:spec-types && jest", "start": "npm run server", "server": "tsx watch --clear-screen=false src/cli.ts server", "client": "tsx src/cli.ts client" diff --git a/src/spec.types.test.ts b/src/spec.types.test.ts new file mode 100644 index 00000000..b6c88689 --- /dev/null +++ b/src/spec.types.test.ts @@ -0,0 +1,657 @@ +// import ts from 'typescript'; + +import * as SDKTypes from "./types.js"; +import * as SpecTypes from "./spec.types.js"; + +// Deep version that recursively removes index signatures (caused by ZodObject.passthrough()) and turns unknowns into `object | undefined` +type DeepKnownKeys = T extends object + ? T extends Array + ? Array> + : T extends Function + ? T + : { + [K in keyof T as string extends K ? never : number extends K ? never : K]: DeepKnownKeys; + } + : unknown extends T + ? (object | undefined) + : T; + +function checkCancelledNotification( + sdk: SDKTypes.CancelledNotification, + spec: SpecTypes.CancelledNotification +) { + sdk = spec; + spec = sdk; +} +function checkBaseMetadata( + sdk: SDKTypes.BaseMetadata, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkImplementation( + sdk: SDKTypes.Implementation, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkProgressNotification( + sdk: SDKTypes.ProgressNotification, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} + +function checkSubscribeRequest( + sdk: SDKTypes.SubscribeRequest, + spec: SpecTypes.SubscribeRequest +) { + sdk = spec; + spec = sdk; +} +function checkUnsubscribeRequest( + sdk: SDKTypes.UnsubscribeRequest, + spec: SpecTypes.UnsubscribeRequest +) { + sdk = spec; + spec = sdk; +} +function checkPaginatedRequest( + sdk: SDKTypes.PaginatedRequest, + spec: SpecTypes.PaginatedRequest +) { + sdk = spec; + spec = sdk; +} +function checkPaginatedResult( + sdk: SDKTypes.PaginatedResult, + spec: SpecTypes.PaginatedResult +) { + sdk = spec; + spec = sdk; +} +function checkListRootsRequest( + sdk: SDKTypes.ListRootsRequest, + spec: SpecTypes.ListRootsRequest +) { + sdk = spec; + spec = sdk; +} +function checkListRootsResult( + sdk: SDKTypes.ListRootsResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkRoot(sdk: SDKTypes.Root, spec: DeepKnownKeys) { + sdk = spec; + spec = sdk; +} +function checkElicitRequest(sdk: SDKTypes.ElicitRequest, spec: DeepKnownKeys) { + sdk = spec; + spec = sdk; +} +function checkElicitResult(sdk: SDKTypes.ElicitResult, spec: DeepKnownKeys) { + sdk = spec; + spec = sdk; +} +function checkCompleteRequest(sdk: SDKTypes.CompleteRequest, spec: DeepKnownKeys) { + sdk = spec; + spec = sdk; +} +function checkCompleteResult(sdk: SDKTypes.CompleteResult, spec: SpecTypes.CompleteResult) { + sdk = spec; + spec = sdk; +} +function checkProgressToken( + sdk: SDKTypes.ProgressToken, + spec: SpecTypes.ProgressToken +) { + sdk = spec; + spec = sdk; +} +function checkCursor(sdk: SDKTypes.Cursor, spec: SpecTypes.Cursor) { + sdk = spec; + spec = sdk; +} +function checkRequest( + sdk: SDKTypes.Request, + spec: SpecTypes.Request +) { + sdk = spec; + spec = sdk; +} +function checkResult( + sdk: SDKTypes.Result, + spec: SpecTypes.Result +) { + sdk = spec; + spec = sdk; +} +function checkRequestId( + sdk: SDKTypes.RequestId, + spec: SpecTypes.RequestId +) { + sdk = spec; + spec = sdk; +} +function checkJSONRPCRequest( + sdk: SDKTypes.JSONRPCRequest, + spec: SpecTypes.JSONRPCRequest +) { + sdk = spec; + spec = sdk; +} +function checkJSONRPCNotification( + sdk: SDKTypes.JSONRPCNotification, + spec: SpecTypes.JSONRPCNotification +) { + sdk = spec; + spec = sdk; +} +function checkJSONRPCResponse( + sdk: SDKTypes.JSONRPCResponse, + spec: SpecTypes.JSONRPCResponse +) { + sdk = spec; + spec = sdk; +} +function checkEmptyResult( + sdk: SDKTypes.EmptyResult, + spec: SpecTypes.EmptyResult +) { + sdk = spec; + spec = sdk; +} +function checkNotification( + sdk: SDKTypes.Notification, + spec: SpecTypes.Notification +) { + sdk = spec; + spec = sdk; +} +function checkClientResult( + sdk: SDKTypes.ClientResult, + spec: SpecTypes.ClientResult +) { + sdk = spec; + spec = sdk; +} +function checkClientNotification( + sdk: SDKTypes.ClientNotification, + spec: SpecTypes.ClientNotification +) { + sdk = spec; + spec = sdk; +} +function checkServerResult( + sdk: SDKTypes.ServerResult, + spec: SpecTypes.ServerResult +) { + sdk = spec; + spec = sdk; +} +function checkResourceTemplateReference( + sdk: SDKTypes.ResourceTemplateReference, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkPromptReference( + sdk: SDKTypes.PromptReference, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkResourceReference( + sdk: SDKTypes.ResourceReference, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkToolAnnotations( + sdk: SDKTypes.ToolAnnotations, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkTool( + sdk: SDKTypes.Tool, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkListToolsRequest( + sdk: SDKTypes.ListToolsRequest, + spec: SpecTypes.ListToolsRequest +) { + sdk = spec; + spec = sdk; +} +function checkListToolsResult( + sdk: SDKTypes.ListToolsResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkCallToolResult( + sdk: SDKTypes.CallToolResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkCallToolRequest( + sdk: SDKTypes.CallToolRequest, + spec: SpecTypes.CallToolRequest +) { + sdk = spec; + spec = sdk; +} +function checkToolListChangedNotification( + sdk: SDKTypes.ToolListChangedNotification, + spec: SpecTypes.ToolListChangedNotification +) { + sdk = spec; + spec = sdk; +} +function checkResourceListChangedNotification( + sdk: SDKTypes.ResourceListChangedNotification, + spec: SpecTypes.ResourceListChangedNotification +) { + sdk = spec; + spec = sdk; +} +function checkPromptListChangedNotification( + sdk: SDKTypes.PromptListChangedNotification, + spec: SpecTypes.PromptListChangedNotification +) { + sdk = spec; + spec = sdk; +} +function checkRootsListChangedNotification( + sdk: SDKTypes.RootsListChangedNotification, + spec: SpecTypes.RootsListChangedNotification +) { + sdk = spec; + spec = sdk; +} +function checkResourceUpdatedNotification( + sdk: SDKTypes.ResourceUpdatedNotification, + spec: SpecTypes.ResourceUpdatedNotification +) { + sdk = spec; + spec = sdk; +} +function checkSamplingMessage( + sdk: SDKTypes.SamplingMessage, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkCreateMessageResult( + sdk: SDKTypes.CreateMessageResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkSetLevelRequest( + sdk: SDKTypes.SetLevelRequest, + spec: SpecTypes.SetLevelRequest +) { + sdk = spec; + spec = sdk; +} +function checkPingRequest( + sdk: SDKTypes.PingRequest, + spec: SpecTypes.PingRequest +) { + sdk = spec; + spec = sdk; +} +function checkInitializedNotification( + sdk: SDKTypes.InitializedNotification, + spec: SpecTypes.InitializedNotification +) { + sdk = spec; + spec = sdk; +} +function checkListResourcesRequest( + sdk: SDKTypes.ListResourcesRequest, + spec: SpecTypes.ListResourcesRequest +) { + sdk = spec; + spec = sdk; +} +function checkListResourcesResult( + sdk: SDKTypes.ListResourcesResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkListResourceTemplatesRequest( + sdk: SDKTypes.ListResourceTemplatesRequest, + spec: SpecTypes.ListResourceTemplatesRequest +) { + sdk = spec; + spec = sdk; +} +function checkListResourceTemplatesResult( + sdk: SDKTypes.ListResourceTemplatesResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkReadResourceRequest( + sdk: SDKTypes.ReadResourceRequest, + spec: SpecTypes.ReadResourceRequest +) { + sdk = spec; + spec = sdk; +} +function checkReadResourceResult( + sdk: SDKTypes.ReadResourceResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkResourceContents( + sdk: SDKTypes.ResourceContents, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkTextResourceContents( + sdk: SDKTypes.TextResourceContents, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkBlobResourceContents( + sdk: SDKTypes.BlobResourceContents, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkResource( + sdk: SDKTypes.Resource, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkResourceTemplate( + sdk: SDKTypes.ResourceTemplate, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkPromptArgument( + sdk: SDKTypes.PromptArgument, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkPrompt( + sdk: SDKTypes.Prompt, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkListPromptsRequest( + sdk: SDKTypes.ListPromptsRequest, + spec: SpecTypes.ListPromptsRequest +) { + sdk = spec; + spec = sdk; +} +function checkListPromptsResult( + sdk: SDKTypes.ListPromptsResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkGetPromptRequest( + sdk: SDKTypes.GetPromptRequest, + spec: SpecTypes.GetPromptRequest +) { + sdk = spec; + spec = sdk; +} +function checkTextContent( + sdk: SDKTypes.TextContent, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkImageContent( + sdk: SDKTypes.ImageContent, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkAudioContent( + sdk: SDKTypes.AudioContent, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkEmbeddedResource( + sdk: SDKTypes.EmbeddedResource, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkResourceLink( + sdk: SDKTypes.ResourceLink, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkContentBlock( + sdk: SDKTypes.ContentBlock, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkPromptMessage( + sdk: SDKTypes.PromptMessage, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkGetPromptResult( + sdk: SDKTypes.GetPromptResult, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkBooleanSchema( + sdk: SDKTypes.BooleanSchema, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkStringSchema( + sdk: SDKTypes.StringSchema, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkNumberSchema( + sdk: SDKTypes.NumberSchema, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkEnumSchema( + sdk: SDKTypes.EnumSchema, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkPrimitiveSchemaDefinition( + sdk: SDKTypes.PrimitiveSchemaDefinition, + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkCreateMessageRequest( + sdk: DeepKnownKeys, // TODO(quirk): some {} type + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkInitializeRequest( + sdk: DeepKnownKeys, // TODO(quirk): some {} type + spec: SpecTypes.InitializeRequest +) { + sdk = spec; + spec = sdk; +} +function checkInitializeResult( + sdk: DeepKnownKeys, // TODO(quirk): some {} type + spec: SpecTypes.InitializeResult +) { + sdk = spec; + spec = sdk; +} +function checkClientCapabilities( + sdk: DeepKnownKeys, // TODO(quirk): {} + spec: SpecTypes.ClientCapabilities +) { + sdk = spec; + spec = sdk; +} +function checkServerCapabilities( + sdk: DeepKnownKeys, // TODO(quirk): {} + spec: SpecTypes.ServerCapabilities +) { + sdk = spec; + spec = sdk; +} +function checkJSONRPCError( + sdk: DeepKnownKeys, // TODO(quirk): error.data + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkJSONRPCMessage( + sdk: DeepKnownKeys, // TODO(quirk): error.data + spec: DeepKnownKeys +) { + sdk = spec; + spec = sdk; +} +function checkClientRequest( + sdk: DeepKnownKeys, // TODO(quirk): capabilities.logging is {} + spec: SpecTypes.ClientRequest +) { + sdk = spec; + spec = sdk; +} +function checkServerRequest( + sdk: DeepKnownKeys, // TODO(quirk): some {} typ + spec: SpecTypes.ServerRequest +) { + sdk = spec; + spec = sdk; +} +function checkLoggingMessageNotification( + sdk: SDKTypes.LoggingMessageNotification, + spec: SpecTypes.LoggingMessageNotification +) { + sdk = spec; + // spec = sdk; // TODO(bug): data is optional +} +function checkServerNotification( + sdk: SDKTypes.ServerNotification, + spec: SpecTypes.ServerNotification +) { + sdk = spec; + // spec = sdk; // TODO(bug): data is optional +} + +// TODO(bug): missing type in SDK +// function checkModelHint( +// sdk: SDKTypes.ModelHint, +// spec: DeepKnownKeys +// ) { +// sdk = spec; +// spec = sdk; +// } + +// TODO(bug): missing type in SDK +// function checkModelPreferences( +// sdk: SDKTypes.ModelPreferences, +// spec: DeepKnownKeys +// ) { +// sdk = spec; +// spec = sdk; +// } + +// TODO(bug): missing type in SDK +// function checkAnnotations( +// sdk: SDKTypes.Annotations, +// spec: DeepKnownKeys +// ) { +// sdk = spec; +// spec = sdk; +// } + +const SPEC_TYPES_FILE = 'src/spec.types.ts'; +const THIS_SOURCE_FILE = 'src/spec.types.test.ts'; + +describe('Spec Types', () => { + const specTypesContent = require('fs').readFileSync(SPEC_TYPES_FILE, 'utf-8'); + const typeNames = [...specTypesContent.matchAll(/export\s+interface\s+(\w+)\b/g)].map(m => m[1]); + const testContent = require('fs').readFileSync(THIS_SOURCE_FILE, 'utf-8'); + + it('should define some expected types', () => { + expect(typeNames).toContain('JSONRPCNotification'); + expect(typeNames).toContain('ElicitResult'); + }); + + for (const typeName of typeNames) { + it(`${typeName} should have a compatibility test`, () => { + expect(testContent).toContain(`function check${typeName}(`); + }); + } +}); \ No newline at end of file