API-patch-page
Update Notion page properties, including title, icon, cover, and trash status, using an API integration. Simplify page management and maintain consistency across workspaces.
Instructions
Notion | Update page properties
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| archived | No | ||
| cover | No | A cover image for the page. Only [external file objects](https://developers.notion.com/reference/file-object) are supported. | |
| icon | No | A page icon for the page. Supported types are [external file object](https://developers.notion.com/reference/file-object) or [emoji object](https://developers.notion.com/reference/emoji-object). | |
| in_trash | No | Set to true to delete a block. Set to false to restore a block. | |
| page_id | Yes | The identifier for the Notion page to be updated. | |
| properties | No | The property values to update for the page. The keys are the names or IDs of the property and the values are property values. If a page property ID is not included, then it is not changed. |
Implementation Reference
- The core handler function for executing any MCP tool call, including 'API-patch-page'. It resolves the tool name to an OpenAPI operation and proxies the HTTP request via HttpClient.this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: params } = request.params // Find the operation in OpenAPI spec const operation = this.findOperation(name) if (!operation) { throw new Error(`Method ${name} not found`) } try { // Execute the operation const response = await this.httpClient.executeOperation(operation, params) // Convert response to MCP format return { content: [ { type: 'text', // currently this is the only type that seems to be used by mcp server text: JSON.stringify(response.data), // TODO: pass through the http status code text? }, ], } } catch (error) { console.error('Error in tool call', error) if (error instanceof HttpClientError) { console.error('HttpClientError encountered, returning structured error', error) const data = error.data?.response?.data ?? error.data ?? {} return { content: [ { type: 'text', text: JSON.stringify({ status: 'error', // TODO: get this from http status code? ...(typeof data === 'object' ? data : { data: data }), }), }, ], } } throw error } })
- src/openapi-mcp-server/mcp/proxy.ts:58-75 (registration)Registration of the listTools handler, which generates and lists all tools including 'API-patch-page' with names like 'API-{operationId}'.this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools: Tool[] = [] // Add methods as separate tools to match the MCP format Object.entries(this.tools).forEach(([toolName, def]) => { def.methods.forEach(method => { const toolNameWithMethod = `${toolName}-${method.name}`; const truncatedToolName = this.truncateToolName(toolNameWithMethod); tools.push({ name: truncatedToolName, description: method.description, inputSchema: method.inputSchema as Tool['inputSchema'], }) }) }) return { tools } })
- Tool schema and lookup generation from OpenAPI spec. Tool names constructed as 'API-' + operationId (e.g., 'API-patch-page'). Input schemas derived from OpenAPI parameters and requestBody.for (const [path, pathItem] of Object.entries(this.openApiSpec.paths || {})) { if (!pathItem) continue for (const [method, operation] of Object.entries(pathItem)) { if (!this.isOperation(method, operation)) continue const mcpMethod = this.convertOperationToMCPMethod(operation, method, path) if (mcpMethod) { const uniqueName = this.ensureUniqueName(mcpMethod.name) mcpMethod.name = uniqueName mcpMethod.description = this.getDescription(operation.summary || operation.description || '') tools[apiName]!.methods.push(mcpMethod) openApiLookup[apiName + '-' + uniqueName] = { ...operation, method, path } zip[apiName + '-' + uniqueName] = { openApi: { ...operation, method, path }, mcp: mcpMethod } } } } return { tools, openApiLookup, zip } }
- Initialization of tools and lookup map in MCPProxy constructor, linking tool names to OpenAPI operations.const converter = new OpenAPIToMCPConverter(openApiSpec) const { tools, openApiLookup } = converter.convertToMCPTools() this.tools = tools this.openApiLookup = openApiLookup this.setupHandlers() }
- Helper function that executes the actual HTTP request for the resolved OpenAPI operation, handling params, file uploads, and errors.async executeOperation<T = any>( operation: OpenAPIV3.OperationObject & { method: string; path: string }, params: Record<string, any> = {}, ): Promise<HttpClientResponse<T>> { const api = await this.api const operationId = operation.operationId if (!operationId) { throw new Error('Operation ID is required') } // Handle file uploads if present const formData = await this.prepareFileUpload(operation, params) // Separate parameters based on their location const urlParameters: Record<string, any> = {} const bodyParams: Record<string, any> = formData || { ...params } // Extract path and query parameters based on operation definition if (operation.parameters) { for (const param of operation.parameters) { if ('name' in param && param.name && param.in) { if (param.in === 'path' || param.in === 'query') { if (params[param.name] !== undefined) { urlParameters[param.name] = params[param.name] if (!formData) { delete bodyParams[param.name] } } } } } } // Add all parameters as url parameters if there is no requestBody defined if (!operation.requestBody && !formData) { for (const key in bodyParams) { if (bodyParams[key] !== undefined) { urlParameters[key] = bodyParams[key] delete bodyParams[key] } } } const operationFn = (api as any)[operationId] if (!operationFn) { throw new Error(`Operation ${operationId} not found`) } try { // If we have form data, we need to set the correct headers const hasBody = Object.keys(bodyParams).length > 0 const headers = formData ? formData.getHeaders() : { ...(hasBody ? { 'Content-Type': 'application/json' } : { 'Content-Type': null }) } const requestConfig = { headers: { ...headers, }, } // first argument is url parameters, second is body parameters const response = await operationFn(urlParameters, hasBody ? bodyParams : undefined, requestConfig) // Convert axios headers to Headers object const responseHeaders = new Headers() Object.entries(response.headers).forEach(([key, value]) => { if (value) responseHeaders.append(key, value.toString()) }) return { data: response.data, status: response.status, headers: responseHeaders, } } catch (error: any) { if (error.response) { console.error('Error in http client', error) const headers = new Headers() Object.entries(error.response.headers).forEach(([key, value]) => { if (value) headers.append(key, value.toString()) }) throw new HttpClientError(error.response.statusText || 'Request failed', error.response.status, error.response.data, headers) } throw error } }