Skip to main content
Glama

Notion ReadOnly MCP Server

by Taewoong1378
parser-multipart.test.ts19.9 kB
import { OpenAPIV3 } from 'openapi-types' import { describe, it, expect } from 'vitest' import { OpenAPIToMCPConverter } from '../parser' describe('OpenAPI Multipart Form Parser', () => { it('converts single file upload endpoint to tool', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/photo': { post: { operationId: 'uploadPetPhoto', summary: 'Upload a photo for a pet', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['photo'], properties: { photo: { type: 'string', format: 'binary', description: 'The photo to upload', }, caption: { type: 'string', description: 'Optional caption for the photo', }, }, }, }, }, }, responses: { '201': { description: 'Photo uploaded successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() expect(Object.keys(tools)).toHaveLength(1) const [tool] = Object.values(tools) expect(tool.methods).toHaveLength(1) const [method] = tool.methods expect(method.name).toBe('uploadPetPhoto') expect(method.description).toContain('Upload a photo for a pet') // Check parameters expect(method.inputSchema.properties).toEqual({ id: { type: 'integer', }, photo: { type: 'string', format: 'uri-reference', description: expect.stringContaining('The photo to upload (absolute paths to local files)'), }, caption: { type: 'string', description: expect.stringContaining('Optional caption'), }, }) expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('photo') expect(method.inputSchema.required).not.toContain('caption') }) it('converts multiple file upload endpoint to tool', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/documents': { post: { operationId: 'uploadPetDocuments', summary: 'Upload multiple documents for a pet', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['documents'], properties: { documents: { type: 'array', items: { type: 'string', format: 'binary', }, description: 'The documents to upload (max 5 files)', }, tags: { type: 'array', items: { type: 'string', }, description: 'Optional tags for the documents', }, }, }, }, }, }, responses: { '201': { description: 'Documents uploaded successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() expect(Object.keys(tools)).toHaveLength(1) const [tool] = Object.values(tools) expect(tool.methods).toHaveLength(1) const [method] = tool.methods expect(method.name).toBe('uploadPetDocuments') expect(method.description).toContain('Upload multiple documents') // Check parameters expect(method.inputSchema.properties).toEqual({ id: { type: 'integer', }, documents: { type: 'array', items: { type: 'string', format: 'uri-reference', description: 'absolute paths to local files', }, description: expect.stringContaining('max 5 files'), }, tags: { type: 'array', items: { type: 'string', }, description: expect.stringContaining('Optional tags'), }, }) expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('documents') expect(method.inputSchema.required).not.toContain('tags') }) it('handles complex multipart forms with mixed content', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/profile': { post: { operationId: 'updatePetProfile', summary: 'Update pet profile with images and data', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['avatar', 'details'], properties: { avatar: { type: 'string', format: 'binary', description: 'Profile picture', }, gallery: { type: 'array', items: { type: 'string', format: 'binary', }, description: 'Additional pet photos', }, details: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' }, breed: { type: 'string' }, }, }, preferences: { type: 'array', items: { type: 'object', properties: { category: { type: 'string' }, value: { type: 'string' }, }, }, }, }, }, }, }, }, responses: { '200': { description: 'Profile updated successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() expect(Object.keys(tools)).toHaveLength(1) const [tool] = Object.values(tools) expect(tool.methods).toHaveLength(1) const [method] = tool.methods expect(method.name).toBe('updatePetProfile') expect(method.description).toContain('Update pet profile') // Check parameters expect(method.inputSchema.properties).toEqual({ id: { type: 'integer', }, avatar: { type: 'string', format: 'uri-reference', description: expect.stringContaining('Profile picture (absolute paths to local files)'), }, gallery: { type: 'array', items: { type: 'string', format: 'uri-reference', description: 'absolute paths to local files', }, description: expect.stringContaining('Additional pet photos'), }, details: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' }, breed: { type: 'string' }, }, additionalProperties: true, }, preferences: { type: 'array', items: { type: 'object', properties: { category: { type: 'string' }, value: { type: 'string' }, }, additionalProperties: true, }, }, }) expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('avatar') expect(method.inputSchema.required).toContain('details') expect(method.inputSchema.required).not.toContain('gallery') expect(method.inputSchema.required).not.toContain('preferences') }) it('handles optional file uploads in multipart forms', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/metadata': { post: { operationId: 'updatePetMetadata', summary: 'Update pet metadata with optional attachments', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['metadata'], properties: { metadata: { type: 'object', required: ['name'], properties: { name: { type: 'string' }, description: { type: 'string' }, }, }, certificate: { type: 'string', format: 'binary', description: 'Optional pet certificate', }, vaccinations: { type: 'array', items: { type: 'string', format: 'binary', }, description: 'Optional vaccination records', }, }, }, }, }, }, responses: { '200': { description: 'Metadata updated successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() const [tool] = Object.values(tools) const [method] = tool.methods expect(method.name).toBe('updatePetMetadata') expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('metadata') expect(method.inputSchema.required).not.toContain('certificate') expect(method.inputSchema.required).not.toContain('vaccinations') expect(method.inputSchema.properties).toEqual({ id: { type: 'integer', }, metadata: { type: 'object', required: ['name'], properties: { name: { type: 'string' }, description: { type: 'string' }, }, additionalProperties: true, }, certificate: { type: 'string', format: 'uri-reference', description: expect.stringContaining('Optional pet certificate (absolute paths to local files)'), }, vaccinations: { type: 'array', items: { type: 'string', format: 'uri-reference', description: 'absolute paths to local files', }, description: expect.stringContaining('Optional vaccination records'), }, }) }) it('handles nested objects with file arrays in multipart forms', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/medical-records': { post: { operationId: 'addMedicalRecord', summary: 'Add medical record with attachments', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['record'], properties: { record: { type: 'object', required: ['date', 'type'], properties: { date: { type: 'string', format: 'date' }, type: { type: 'string' }, notes: { type: 'string' }, attachments: { type: 'array', items: { type: 'object', required: ['file', 'type'], properties: { file: { type: 'string', format: 'binary', }, type: { type: 'string', enum: ['xray', 'lab', 'prescription'], }, description: { type: 'string' }, }, }, }, }, }, }, }, }, }, }, responses: { '201': { description: 'Medical record added successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() const [tool] = Object.values(tools) const [method] = tool.methods expect(method.name).toBe('addMedicalRecord') expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('record') // Verify nested structure is preserved const recordSchema = method.inputSchema.properties!.record as any expect(recordSchema.type).toBe('object') expect(recordSchema.required).toContain('date') expect(recordSchema.required).toContain('type') // Verify nested file array structure const attachmentsSchema = recordSchema.properties.attachments expect(attachmentsSchema.type).toBe('array') expect(attachmentsSchema.items.type).toBe('object') expect(attachmentsSchema.items.properties.file.format).toBe('uri-reference') expect(attachmentsSchema.items.properties.file.description).toBe('absolute paths to local files') expect(attachmentsSchema.items.required).toContain('file') expect(attachmentsSchema.items.required).toContain('type') }) it('handles oneOf/anyOf schemas with file uploads', () => { const spec: OpenAPIV3.Document = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/pets/{id}/content': { post: { operationId: 'addPetContent', summary: 'Add pet content (photo or document)', parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'integer' }, }, ], requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['content'], properties: { content: { oneOf: [ { type: 'object', required: ['photo', 'isProfile'], properties: { photo: { type: 'string', format: 'binary', }, isProfile: { type: 'boolean', }, }, }, { type: 'object', required: ['document', 'category'], properties: { document: { type: 'string', format: 'binary', }, category: { type: 'string', enum: ['medical', 'training', 'adoption'], }, }, }, ], }, }, }, }, }, }, responses: { '201': { description: 'Content added successfully', }, }, }, }, }, } const converter = new OpenAPIToMCPConverter(spec) const { tools } = converter.convertToMCPTools() const [tool] = Object.values(tools) const [method] = tool.methods expect(method.name).toBe('addPetContent') expect(method.inputSchema.required).toContain('id') expect(method.inputSchema.required).toContain('content') // Verify oneOf structure is preserved const contentSchema = method.inputSchema.properties!.content as any expect(contentSchema.oneOf).toHaveLength(2) // Check photo option const photoOption = contentSchema.oneOf[0] expect(photoOption.type).toBe('object') expect(photoOption.properties.photo.format).toBe('uri-reference') expect(photoOption.properties.photo.description).toBe('absolute paths to local files') expect(photoOption.required).toContain('photo') expect(photoOption.required).toContain('isProfile') // Check document option const documentOption = contentSchema.oneOf[1] expect(documentOption.type).toBe('object') expect(documentOption.properties.document.format).toBe('uri-reference') expect(documentOption.properties.document.description).toBe('absolute paths to local files') expect(documentOption.required).toContain('document') expect(documentOption.required).toContain('category') }) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Taewoong1378/notion-readonly-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server