diff --git a/README.md b/README.md index 04d81196..4d35178e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ This project implements an [MCP server](https://spec.modelcontextprotocol.io/) f - All database operations now use `data_source_id` instead of `database_id` - Search filter values changed from `["page", "database"]` to `["page", "data_source"]` - Page creation now supports both `page_id` and `database_id` parents (for data sources) +- Data source creation payload now uses `initial_data_source.properties` instead of top-level `properties` ### Do I need to migrate? @@ -61,7 +62,7 @@ If you have hardcoded tool names or prompts that reference the old database tool | -------------- | --------------- | ---------------- | | `post-database-query` | `query-data-source` | `database_id` → `data_source_id` | | `update-a-database` | `update-a-data-source` | `database_id` → `data_source_id` | -| `create-a-database` | `create-a-data-source` | No change (uses `parent.page_id`) | +| `create-a-database` | `create-a-data-source` | payload uses `initial_data_source.properties` | > **Note:** `retrieve-a-database` is still available and returns database metadata including the list of data source IDs. Use `retrieve-a-data-source` to get the schema and properties of a specific data source. diff --git a/scripts/notion-openapi.json b/scripts/notion-openapi.json index 18669f22..d6525a7e 100644 --- a/scripts/notion-openapi.json +++ b/scripts/notion-openapi.json @@ -1812,7 +1812,9 @@ }, "deprecated": false, "security": [] - }, + } + }, + "/v1/databases/{data_source_id}": { "patch": { "summary": "Update a data source", "description": "Update properties of a data source", @@ -1904,7 +1906,7 @@ "security": [] } }, - "/v1/data_sources": { + "/v1/databases": { "post": { "summary": "Create a data source", "description": "Create a new data source (database)", @@ -1924,15 +1926,23 @@ "type": "object", "required": [ "parent", - "properties" + "initial_data_source" ], "properties": { "parent": { "$ref": "#/components/schemas/pageIdParentRequest" }, - "properties": { + "initial_data_source": { "type": "object", - "description": "Property schema of data source" + "required": [ + "properties" + ], + "properties": { + "properties": { + "type": "object", + "description": "Property schema of data source" + } + } }, "title": { "type": "array", @@ -2214,4 +2224,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/openapi-mcp-server/openapi/__tests__/notion-openapi-data-source.test.ts b/src/openapi-mcp-server/openapi/__tests__/notion-openapi-data-source.test.ts new file mode 100644 index 00000000..50c2c14e --- /dev/null +++ b/src/openapi-mcp-server/openapi/__tests__/notion-openapi-data-source.test.ts @@ -0,0 +1,62 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' + +type NotionOpenApiSpec = { + paths: Record> +} + +function loadSpec(): NotionOpenApiSpec { + const filename = fileURLToPath(import.meta.url) + const directory = path.dirname(filename) + const specPath = path.resolve(directory, '../../../../scripts/notion-openapi.json') + return JSON.parse(fs.readFileSync(specPath, 'utf-8')) as NotionOpenApiSpec +} + +function findOperationPath(spec: NotionOpenApiSpec, operationId: string): { path: string; method: string; operation: any } | null { + for (const [pathName, pathItem] of Object.entries(spec.paths)) { + for (const [method, operation] of Object.entries(pathItem)) { + if (operation?.operationId === operationId) { + return { path: pathName, method, operation } + } + } + } + return null +} + +describe('Notion OpenAPI data-source compatibility', () => { + it('maps create-a-data-source to the databases endpoint', () => { + const spec = loadSpec() + const op = findOperationPath(spec, 'create-a-data-source') + + expect(op).not.toBeNull() + expect(op?.method).toBe('post') + expect(op?.path).toBe('/v1/databases') + }) + + it('maps update-a-data-source to the databases endpoint', () => { + const spec = loadSpec() + const op = findOperationPath(spec, 'update-a-data-source') + + expect(op).not.toBeNull() + expect(op?.method).toBe('patch') + expect(op?.path).toBe('/v1/databases/{data_source_id}') + }) + + it('accepts create payload via initial_data_source.properties', () => { + const spec = loadSpec() + const op = findOperationPath(spec, 'create-a-data-source') + const schema = op?.operation?.requestBody?.content?.['application/json']?.schema + + expect(schema).toBeDefined() + expect(schema.required).toContain('parent') + expect(schema.required).toContain('initial_data_source') + expect(schema.required).not.toContain('properties') + + expect(schema.properties.initial_data_source).toBeDefined() + expect(schema.properties.initial_data_source.type).toBe('object') + expect(schema.properties.initial_data_source.properties).toBeDefined() + expect(schema.properties.initial_data_source.properties.properties).toBeDefined() + }) +})