# @doist/mcp-core 中文文档

规范地址：https://npmcn.com/package/%40doist/mcp-core/

版本：0\.4\.1
npm 元数据更新时间：2026-11-11T13:44:15.596Z
中文译文更新时间：2026-11-12T15:17:05.231Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40doist/mcp-core
原文对照：https://npmcn.com/package/%40doist/mcp-core/?lang=original
项目主页：https://github.com/todoist/mcp-core#readme
源码仓库：https://github.com/todoist/mcp-core

弃用说明：Moved to @todoist/mcp-core\. Replace @doist/mcp-core with @todoist/mcp-core in your dependencies and imports\.

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：100 kB，79 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24
- 运行依赖：0 个
- peer 依赖：zod ^4\.3\.0，@modelcontextprotocol/server ^2\.2\.0
- 首次发布：2026-11-08T17:11:17.631Z
- 最新版本发布：2026-11-11T11:32:45.121Z
- 近 30 天下载量：5535（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：8（近 12 个月 8 个）

## 中文简介

Doist MCP 服务器共享核心

## 安装

```sh
npm install @doist/mcp-core
```

也可以使用：`pnpm add @doist/mcp-core`、`yarn add @doist/mcp-core`、`bun add @doist/mcp-core`

## README

# @ doist/ mcp 分数

Todoist MCP服务器共享核心([待办事宜- mcp](https://github.com/todoist/todoist-mcp), (中文(简体) ), [通讯- mcp](https://github.com/todoist/comms-mcp) 和Automations MCP服务器:工具合同,工具注册,工具结果如何塑造给电线,合同一个服务器提供为它服务的主机,合同测试套件每个服务器运行在自己的工具上.

TypeScript, 仅限无害环境管理, 第24节, npm 第11节.

## 安装

```bash
npm install @doist/mcp-core
```

MCP SDK和Zod是同行的依赖关系,因此服务器和核心各共享一份. 这很重要:一个为多个 MCP 服务器服务的主机将它们解开 `instanceof McpServer`与第二本SDK相对抗的失败.

| 端点 | 范围 |
| --- | --- |
| `@modelcontextprotocol/server` | `^2.2.0` |
| `zod` | `^4.3.0` |

`./testing` 进口 `vitest` (4.1或以后)和 `@modelcontextprotocol/client` (2.2或后),来自服务器本身的devDependency. 他们故意不是同伴: npm在生产中安装了生产依赖的同伴,所以宣布他们将在每个服务器的Vitest中放入 `npm ci --omit=dev`。 。 。 .

## 里面有什么东西

| 模块 | 主要出口 |
| --- | --- |
| `tool` | `McpTool`, (中文(简体) ), `AnyMcpTool`, (中文(简体) ), `ToolResult`, (中文(简体) ), `ExecuteResult`, (中文(简体) ), `RequiredToolAnnotations`, (中文(简体) ), `ToolSchemaSource` (类型) |
| `register-tool` | `createToolRegistrar`, (中文(简体) ), `ToolRegistrarOptions`, (中文(简体) ), `RegisterTool`, (中文(简体) ), `ToolFailure` |
| `tool-output` | `getToolOutput`, (中文(简体) ), `getErrorOutput`, (中文(简体) ), `ToolOutputOptions` |
| `sanitize` | `removeNullFields`, (中文(简体) ), `RemoveNullFieldsOptions` |
| `tool-schemas` | `getToolSchemas`, (中文(简体) ), `ToolSchemas` |
| `annotations` | `getMcpAnnotations`, (中文(简体) ), `formatToolTitle` |
| `app-meta` | `normalizeToolMeta` |
| `host` (顺从) | `validateBearerToken`, (中文(简体) ), `ValidateBearerTokenOptions`, (中文(简体) ), `TokenValidationError`, (中文(简体) ), `TokenValidationResult`, (中文(简体) ), `ValidateToken`, (中文(简体) ), `McpServerOptions`, (中文(简体) ), `isTransientValidationError`, (中文(简体) ), `buildBearerChallenge`, (中文(简体) ), `createTokenVerdictCache` |
| `testing` (顺从) | `describeOutputSchemaNullability`, (中文(简体) ), `describeAdvertisedToolSchemas`, (中文(简体) ), `describeToolAnnotations`, (中文(简体) ), `describeTokenFootprint`, (中文(简体) ), `measureTokenFootprint`, (中文(简体) ), `findNullablePaths`, (中文(简体) ), `listAdvertisedTools` |

## 使用量

### 定义工具

`McpTool<Params, Output, Context>` 是对每个呼叫的通用,所以每个服务器会选择自己的上下文:一个API客户端,或者一个携带一个带有中止信号和进度报告器的对象. 一个服务器通常用所填上下文来代替它.

```ts
import type { McpTool } from '@doist/mcp-core'
import { z } from 'zod'
import type { NotesApi } from './notes-api.js'

type NotesTool<P extends z.ZodRawShape, O extends z.ZodRawShape = Record<string, never>> = McpTool<
    P,
    O,
    NotesApi
>

export const findNotes = {
    name: 'find-notes',
    description: 'Finds notes whose text contains the search term.',
    parameters: { searchText: z.string().describe('Text to look for.') },
    outputSchema: { notes: z.array(z.object({ id: z.string(), text: z.string() })) },
    annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
    async execute({ searchText }, api) {
        const notes = await api.search(searchText)
        return { textContent: `Found ${notes.length} notes.`, structuredContent: { notes } }
    },
} satisfies NotesTool<
    { searchText: z.ZodString },
    { notes: z.ZodArray<z.ZodObject<{ id: z.ZodString; text: z.ZodString }>> }
>
```

### 登记工具

`createToolRegistrar` 返回在服务器上注册工具的函数。 产品之间有差异的一切都通过它的选择:

```ts
import { createToolRegistrar } from '@doist/mcp-core'
import { McpServer } from '@modelcontextprotocol/server'

export function getMcpServer({ token }: { token: string }): McpServer {
    const server = new McpServer({ name: 'notes-mcp-server', version: '1.0.0' })
    const api = new NotesApi(token)

    const register = createToolRegistrar<NotesApi>({
        titlePrefix: 'Notes',
        createContext: () => api,
        formatError: (error) => (error instanceof Error ? error.message : 'Something went wrong.'),
        // Optional: runs around every call, for retries, request tagging or rewriting the result.
        wrapExecute: (tool, run) => withRetry(run),
        // Optional: replaces the default log line, which never includes the arguments.
        onError: ({ tool, error }) => logger.error({ tool: tool.name, error }),
    })
    for (const tool of [findNotes]) register(server, tool)

    return server
}
```

对于每个工具:

-   将输入和输出计划汇编成JSON Schema 2020-12,每个进程一次,但许多服务器都予以登记;
-   标题 "`<titlePrefix>`数字 : `<Title Case Name>`" 和默认 `openWorldHint` 作假,工具本身的注解获胜;
-   填写 MCP Apps UI 资源的两种形式(`_meta.ui.resourceUri` 财务报告和审定财务报表 `_meta['ui/resourceUri']`当工具设置时;
-   形状 `getToolOutput`,以下;
-   抓到任何东西 `execute` 扔出,传到 `onError`返回时 `formatError`给模型的文本作为 `isError` 结果.

`createContext` 收到工具和SDK的 `ServerContext`,所以一个长期工具可以读取 `serverContext.mcpReq.signal` 或发送进度通知.

### 结果的线形

`getToolOutput` 转出 `ToolResult` 改为 a `CallToolResult`数字 :

1.  裸体和空物体被剥去 `structuredContent` (单位:千美元)`removeNullFields`) (中文(简体) ). 空数组留相. 过 `verbatimKeys` 给那些虚构的钥匙 比如一个存储的JSON Schema.
2.  `content` 按照顺序:工具本身 `contentItems`,则 `structuredContent` 作为JSON文本块,那么 `textContent`。只读取文本的客户端 `content[0]` (OpenAI 响应 API 下降) `structuredContent`仍然得到有效载荷.
3.  `structuredContentAsText: false` 离开JSON的封锁, 用于部署 其客户全部读取 `structuredContent`。 。 。 .

### 从主机为服务器服务

`@doist/mcp-core/host` 是服务器软件包与服务于其中几个软件主机之间的合同,例如 todoist-ai-Institutions。 每个服务器输出相同的两个形状:

-   `getMcpServer(options)`,则 `McpServerOptions` (单位:千美元)`{ token, baseUrl? }`),或 `McpServerOptions & { ... }` 当需要更多时;
-   (单位:千美元) `ValidateToken`数字 : `(token, baseUrl?) => Promise<'valid' | 'invalid' | 'forbidden'>`。 。 。 .

验证者只在符上做出裁决并拒绝其他任何内容,所以主机可以重试一次失败而不把它作为裁决. `validateBearerToken` 仅从状态构建一个 `GET`数字 :

```ts
import { validateBearerToken, type ValidateToken } from '@doist/mcp-core/host'

export const validateNotesToken: ValidateToken = (token, baseUrl = 'https://notes.example.com') =>
    validateBearerToken({ url: `${baseUrl}/api/v1/me`, token })
```

| API 答案 | `validateBearerToken` |
| --- | --- |
| 第2条 | `valid` |
| 401 (中文(简体) ) | `invalid`: 主机发送401个挑战,客户端刷新其符号 |
| 403 (韩语) | `forbidden`,或任何东西 `classifyForbidden(response)` 返回时 |
| 任何其它状态,包含重定向 | 掷出 `TokenValidationError`,其 `httpStatusCode` 让主机将 5xx 作为瞬态 |
| 没有 `timeoutMs` (第5类) | 拒绝 `TimeoutError` |
| 网络失败 | 以未更改的错误拒绝 |

身体被排出未读取,所以改变反应的外形不能使一个符号被错误地分类,连接会回到池中去. 重新定向没有被遵循:一个签入的页面,如果答案200的话会通过一个被接受的符号. 过 `classifyForbidden` 当一个403也可以意味着拒绝 同意不能解除, `headers` 标记请求,以及 `fetch` 当服务器的 API 客户端有自己的传输(从 `HTTPS_PROXY`因此,验证的方式 与其它呼叫一样.

在主机方面,有三个帮助者将验证器的答案变成响应,无论HTTP框架如何:

```ts
import {
    buildBearerChallenge,
    createTokenVerdictCache,
    isTransientValidationError,
} from '@doist/mcp-core/host'

const verdicts = createTokenVerdictCache({ ttlMs: 120_000, maxEntries: 10_000 })

async function checkToken(token: string, baseUrl: string): Promise<Response | 'proceed'> {
    let result = verdicts.get(token, baseUrl)
    if (!result) {
        try {
            result = await validateNotesToken(token, baseUrl)
        } catch (error) {
            if (isTransientValidationError(error)) return 'proceed' // or a 503; never cached
            throw error
        }
        verdicts.set(token, baseUrl, result)
    }
    if (result === 'valid') return 'proceed'
    const forbidden = result === 'forbidden'
    return new Response(null, {
        status: forbidden ? 403 : 401,
        headers: {
            'WWW-Authenticate': buildBearerChallenge({
                error: forbidden ? 'insufficient_scope' : undefined,
                resourceMetadataUrl:
                    'https://notes.example.com/.well-known/oauth-protected-resource/mcp',
            }),
        },
    })
}
```

-   `isTransientValidationError`:超时,取消或拒绝连接,或5xx,拒绝值重试. 转机或404不是.
-   `buildBearerChallenge`数字 : `Bearer`,与 `error="insufficient_scope"` 财务报告和审定财务报表 `resource_metadata="…"` 当给予.
-   `createTokenVerdictCache`: 以符号和基准 URL 键入的判决,保存于 `ttlMs`最古老的落地时满地.

### 合同测试

`@doist/mcp-core/testing` 持有服务器运行在它自己的工具注册簿上的套件 :

```ts
import {
    describeAdvertisedToolSchemas,
    describeOutputSchemaNullability,
} from '@doist/mcp-core/testing'
import { getMcpServer } from './mcp-server.js'
import { registeredTools } from './tool-registry.js'

describeOutputSchemaNullability(registeredTools)
describeAdvertisedToolSchemas({
    tools: registeredTools,
    createServer: () => getMcpServer({ token: 'test-token' }),
})
```

-   `describeOutputSchemaNullability`: 每个工具一个测试, 任何允许输出字段都失败 `null`。 。 。 .
-   `describeAdvertisedToolSchemas`:服务器完全广告这些工具,每个工具都有JSON Schema 2020-12 schemas匹配了直接Zod转换.

都拿一个 `{ name, parameters, outputSchema? }`,这样服务器就可以在工具移动到 `McpTool`。 。 。 .

有两个只读客户端收到的东西,所以他们不重复注册的内容:

```ts
import { describeTokenFootprint, describeToolAnnotations } from '@doist/mcp-core/testing'
import { encode } from 'gpt-tokenizer'

const createServer = () => getMcpServer({ token: 'test-token' })

describeTokenFootprint({
    createServer,
    countTokens: (text) => encode(text).length,
    budget: 13_250,
})
describeToolAnnotations({
    createServer,
    expected: {
        'find-notes': {
            title: 'Notes: Find Notes',
            readOnlyHint: true,
            destructiveHint: false,
            idempotentHint: true,
        },
    },
})
```

-   `describeTokenFootprint`: 每届会议支付的固定费用( `tools/list` 有效载荷为一个阵列,加 `instructions`保留在 `budget`一半失踪也失败了 它打印每个工具的成本。 将预算视为权宜之计:需要增加预算,以证明造成预算变化的理由. `measureTokenFootprint` 返回相同的数字。 标语器是你的,所以Mcp分数 取决于无.
-   `describeToolAnnotations`:每个广告工具都包含准确的预期说明、标题和缺省(`openWorldHint` 默认为虚假),每个预期工具都会被公示. 客户端从这些决定一个电话是否需要用户的确认.

## 工具规则

每个 Doist MCP 服务器都持有这个 核心可以执行一个, 它说如何.

-   **输出计划从未使用 `.nullable()`。 。 。 .** 结果在SDK验证它们之前被剥去无效值,所以一个无效的字段会保证一个工具永远无法发送的值,一旦剥去"输出验证错误",则调用失败. 申报字段 `.optional()` 并留出密钥(`foo: value ?? undefined`) (中文(简体) ). `describeOutputSchemaNullability` 执行这个.
-   **说明描述了该工具最坏的情况.** 它们是每个工具,而不是每个行动,所以一个将几个行动分组的工具被附加了最危险的一个. `readOnlyHint: true` 仅当无事持续存在,且无事发生(文件或可共享链接计数)时. `openWorldHint: true` 当工具到达用户自己账户以外的任何人时:通知他人,或制作公共URL. `destructiveHint: true` 当它删除、覆盖、替换先前状态、删除访问权限或以其他方式难以撤消时;发送通知是添加剂而不是破坏性的. `RequiredToolAnnotations` 3个提示是强制性的.
-   **描述一个输出字段,只在计划不能时进行.** 输出计划发送到每个 `tools/list`,共享的则每个工具内嵌一次。 描述单位和格式、惯例、结合、有条件的存在和不明显的含义;保留任何重复字段名称或列出enum值的内容。 输入描述是一个模型如何学会调用工具,应该像需要的那样完整.
-   **用字符串清除可选字段, 不 `null`。 。 。 .** 一些提供商,特别是双子座公司,对无效输入处理不当. 使用描述式哨兵( I)`"unassign"`, (中文(简体) ), `"remove"`),并用实地描述记录.
-   **批量写入工具报告每个项目失败, 绝不扔出 .** 单枪匹马地解决每件物品,把那些失败的同那些成功的东西一起归还,即使每件物品都失败。 被抛出的错误隐藏了写的东西,所以重试一次写了两次.
-   **不默认日志工具参数 .** 写作工具中包含用户内容. 默认 `onError` 只记录工具名称和出错.

## 许可证

麻省理工学院

## 常见问题

### @doist/mcp-core 是什么？

Doist MCP 服务器共享核心注意：该包已被作者标记为弃用——Moved to @todoist/mcp-core\. Replace @doist/mcp-core with @todoist/mcp-core in your dependencies and imports\.

### 如何安装 @doist/mcp-core？

在项目目录运行 npm install @doist/mcp-core，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：zod（^4\.3\.0）、@modelcontextprotocol/server（^2\.2\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @doist/mcp-core 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @doist/mcp-core 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @doist/mcp-core 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @doist/mcp-core 可以商用吗？

@doist/mcp-core 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @doist/mcp-core 的下载量有多少？

根据 npm 官方下载统计，@doist/mcp-core 在 2026/10/13 – 2026/11/11 这 30 天内被下载 5,535 次，日均约 185 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @doist/mcp-core 还在维护吗？

最新版本 0\.4\.1 发布于 2026/11/11（2 天前）；至今共发布 8 个版本，其中过去 12 个月发布了 8 个；首次发布于 2026/11/09。作者已将其标记为弃用，新项目建议寻找替代方案。

