文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

55 行
2.0 KiB
TypeScript

import { Request, RequestMethod } from '../Api';
import { ErrorBadRequest, ErrorForbidden, ErrorMethodNotAllowed } from '../utils/errors';
import Setting from '../../../models/Setting';
import McpServer from '../../mcp/McpServer';
import { JsonRpcRequest, JsonRpcResponse } from '../../mcp/types';
import ApiResponse from '../ApiResponse';
// The MCP spec requires notifications to be acknowledged with an empty 202.
// Strict clients such as Codex close the transport if they get a 200 instead.
const acceptedResponse = () => {
const response = new ApiResponse();
response.status = 202;
response.body = '';
return response;
};
// Single-endpoint JSON-RPC transport. v1 only handles client-initiated
// requests so plain POST/response is enough; streamable HTTP can come later
// if we ever need server-initiated messages.
export default async function(request: Request) {
if (request.method !== RequestMethod.POST) throw new ErrorMethodNotAllowed();
if (!(Setting.value('mcp.enabled') as boolean)) {
throw new ErrorForbidden('MCP server is disabled');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- JSON-RPC envelope is dynamically shaped
let payload: any;
try {
payload = typeof request.body === 'string' ? JSON.parse(request.body) : request.body;
} catch (error) {
throw new ErrorBadRequest('Invalid JSON body');
}
const server = McpServer.instance();
// JSON-RPC batch: spec allows an array of requests. If every item is a
// notification, the server must return nothing (empty body), same as the
// single-notification case.
if (Array.isArray(payload)) {
const responses: JsonRpcResponse[] = [];
for (const item of payload) {
const r = await server.handleRequest(item as JsonRpcRequest);
if (r) responses.push(r);
}
if (!responses.length) return acceptedResponse();
return responses;
}
const response = await server.handleRequest(payload as JsonRpcRequest);
// Notifications get no body per JSON-RPC spec.
if (!response) return acceptedResponse();
return response;
}