Skip to main content
Glama
index.ts10.4 kB
#!/usr/bin/env node import 'dotenv/config'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { NotionService } from './notion-service.js'; // 工具參數的 schema 定義 const GetPageSchema = z.object({ pageId: z.string().describe('Notion page ID to retrieve'), }); const SearchPagesSchema = z.object({ query: z.string().describe('Search query for pages'), filter: z.object({ property: z.string().optional(), value: z.any().optional(), }).optional(), }); const CreatePageSchema = z.object({ parent: z.object({ database_id: z.string().optional(), page_id: z.string().optional(), }), properties: z.record(z.any()), children: z.array(z.any()).optional(), }); const UpdatePageSchema = z.object({ pageId: z.string().describe('Page ID to update'), properties: z.record(z.any()), }); const QueryDatabaseSchema = z.object({ databaseId: z.string().describe('Database ID to query'), filter: z.any().optional(), sorts: z.array(z.any()).optional(), startCursor: z.string().optional(), pageSize: z.number().optional(), }); class NotionMCPServer { private server: Server; private notionService: NotionService; constructor() { this.server = new Server( { name: 'notion-mcp-server', version: '0.1.0', }, { capabilities: { tools: {}, resources: {}, }, } ); this.notionService = new NotionService(); this.setupToolHandlers(); this.setupResourceHandlers(); // 錯誤處理 this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { // 列出所有工具 this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get-page', description: 'Get a Notion page by ID', inputSchema: { type: 'object', properties: { pageId: { type: 'string', description: 'The ID of the Notion page to retrieve', }, }, required: ['pageId'], }, }, { name: 'search-pages', description: 'Search for Notion pages', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query', }, filter: { type: 'object', description: 'Optional filter criteria', properties: { property: { type: 'string' }, value: {}, }, }, }, required: ['query'], }, }, { name: 'create-page', description: 'Create a new Notion page', inputSchema: { type: 'object', properties: { parent: { type: 'object', properties: { database_id: { type: 'string' }, page_id: { type: 'string' }, }, }, properties: { type: 'object', description: 'Page properties', }, children: { type: 'array', description: 'Page content blocks', }, }, required: ['parent', 'properties'], }, }, { name: 'update-page', description: 'Update a Notion page', inputSchema: { type: 'object', properties: { pageId: { type: 'string', description: 'The ID of the page to update', }, properties: { type: 'object', description: 'Properties to update', }, }, required: ['pageId', 'properties'], }, }, { name: 'query-database', description: 'Query a Notion database', inputSchema: { type: 'object', properties: { databaseId: { type: 'string', description: 'The ID of the database to query', }, filter: { type: 'object', description: 'Optional filter criteria', }, sorts: { type: 'array', description: 'Optional sort criteria', }, startCursor: { type: 'string', description: 'Pagination cursor', }, pageSize: { type: 'number', description: 'Number of results per page', }, }, required: ['databaseId'], }, }, ], }; }); // 處理工具調用 this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get-page': { const { pageId } = GetPageSchema.parse(args); const page = await this.notionService.getPage(pageId); return { content: [ { type: 'text', text: JSON.stringify(page, null, 2), }, ], }; } case 'search-pages': { const { query, filter } = SearchPagesSchema.parse(args); const results = await this.notionService.searchPages(query, filter); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } case 'create-page': { const { parent, properties, children } = CreatePageSchema.parse(args); const page = await this.notionService.createPage(parent, properties, children); return { content: [ { type: 'text', text: `Page created successfully: ${JSON.stringify(page, null, 2)}`, }, ], }; } case 'update-page': { const { pageId, properties } = UpdatePageSchema.parse(args); const page = await this.notionService.updatePage(pageId, properties); return { content: [ { type: 'text', text: `Page updated successfully: ${JSON.stringify(page, null, 2)}`, }, ], }; } case 'query-database': { const { databaseId, filter, sorts, startCursor, pageSize } = QueryDatabaseSchema.parse(args); const results = await this.notionService.queryDatabase( databaseId, filter, sorts, startCursor, pageSize ); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); } private setupResourceHandlers() { // 列出所有資源 this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'notion://recent-pages', name: 'Recent Pages', description: 'Recently accessed Notion pages', mimeType: 'application/json', }, { uri: 'notion://databases', name: 'Databases', description: 'List of accessible Notion databases', mimeType: 'application/json', }, ], }; }); // 讀取資源 this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; try { switch (uri) { case 'notion://recent-pages': { const pages = await this.notionService.getRecentPages(); return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify(pages, null, 2), }, ], }; } case 'notion://databases': { const databases = await this.notionService.getDatabases(); return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify(databases, null, 2), }, ], }; } default: throw new Error(`Unknown resource: ${uri}`); } } catch (error) { throw new Error(`Failed to read resource ${uri}: ${error instanceof Error ? error.message : String(error)}`); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Notion MCP server running on stdio'); } } const server = new NotionMCPServer(); server.run().catch(console.error);

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/589411/notion_mcp_connection'

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