一些检查失败了
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
55 行
2.0 KiB
TypeScript
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;
|
|
}
|