---
author: OOMOL
author_url: https://oomol.com/zh-cn/about/
title: 使用 ProjectConnector 连接终端用户账号
description: 在 TypeScript 后端使用 ProjectConnector，为 SaaS 用户连接账号并代用户执行操作。
lang: zh-CN
canonical_url: https://oomol.com/zh-cn/docs/project-connector/
markdown_url: https://oomol.com/zh-cn/docs/project-connector.md
---

# 使用 ProjectConnector 连接终端用户账号

当产品里的每个用户都要连接自己的 provider 账号时，使用 `ProjectConnector`。你的后端通过 `externalUserId` 标识用户，创建授权链接，保存 connected account ID，再代该用户执行 action。

这条路径使用形如 `oo_proj_…` 的 project API key，与 `Connector` 使用的个人 `api_…` key 相互独立。

## 接入自己的 Agent

ProjectConnector 已封装连接服务的调用。OOMOL 处理第三方账号授权、凭据保存、token 刷新和操作执行。你的产品负责用户登录鉴权、账号绑定和 Agent 工具 handler。

Agent 在后端运行时，工具 handler 可以直接调用 ProjectConnector。Agent 在用户设备运行时，将工具请求发到产品自己的鉴权后端。Project API key 始终保存在该后端。

托管的个人/团队 MCP 配置使用 OOMOL 用户和团队连接。SaaS 终端用户账号通过 ProjectConnector 或 SaaS HTTP 接口调用。客户端要求 MCP 时，需要在你的后端提供面向产品用户、带鉴权的 MCP 适配层。

## 准备 project

编写运行期流程前，先在 OOMOL Console 创建：

1. 一个 Connector project。
2. 用户可以连接的每个 service 对应的 provider config。
3. 一个保存在后端密钥管理系统中的 project API key。

[Connector for SaaS 使用指南](/zh-cn/docs/connector-saas/)包含 Console 配置步骤与对应的 REST 请求。

## 安装并初始化

```sh
npm install @oomol-lab/connector
```

```ts
import { ProjectConnector } from "@oomol-lab/connector";

const project = new ProjectConnector({
  apiKey: process.env.OOMOL_PROJECT_API_KEY!,
});
```

## 创建 OAuth 授权请求

使用业务数据库中的稳定用户 ID 作为 `externalUserId`：

```ts
const request = await project.connect.oauth("user_42", {
  service: "gmail",
  connectionName: "work",
  returnUri: "https://app.example.com/connected",
});

redirectUserTo(request.authorizationUrl);
```

用户完成授权后，等待请求进入最终状态：

```ts
const connected = await project.waitForConnection(request);

if (connected.status === "connected") {
  saveConnectedAccountId(connected.connectedAccountId);
}
```

`project.connect.oauth` 返回的是等待用户完成的授权请求。授权成功后，`waitForConnection` 才会在请求结果中返回 `connectedAccountId`。

对于 API key 和自定义凭据类型的 provider，使用 `connect.apiKey` 或 `connect.customCredential`。这两个方法会同步验证凭据并返回 connected account。

## 代一个用户执行 action

```ts
const result = await project.execute(
  "user_42",
  "gmail.search_threads",
  { query: "is:unread" },
  { connectedAccountId: "ca-1" },
);
```

已经保存 `connectedAccountId` 时，优先显式传入。它会选择一个确定的账号，避免依赖“最新 active 账号”。如果产品使用稳定 alias，也可以传 `connectionName`。

同一个请求或任务需要执行多次操作时，可以通过 `forUser` 绑定一次用户：

Slack 示例要求该用户已有名为“work”的连接。请使用后端为该 Slack 账号保存的连接名称。forUser 只绑定用户；账号选择器应传入 execute 的选项。

```ts
const user = project.forUser("user_42");
await user.execute(
  "slack.post_message",
  { channel: "#general", text: "shipped" },
  { connectionName: "work" },
);
```

## 将操作接入 Agent 工具

下面是与框架无关的工具描述，展示需要注册到 Agent 框架的 schema 和 handler。创建前，先鉴权请求、确认用户可以执行该操作，再从数据库读取该用户的 Gmail 账号绑定。`currentUser.id` 是后端确定的 external user ID；`storedAccount.connectedAccountId` 属于该用户。这两个值都不由模型参数指定。

```ts
const searchMailTool = {
  name: "search_mail",
  description: "Search the current user's Gmail threads.",
  inputSchema: {
    type: "object",
    properties: { query: { type: "string" } },
    required: ["query"],
    additionalProperties: false,
  },
  execute: async (input: { query: string }) =>
    project.forUser(currentUser.id).execute(
      "gmail.search_threads",
      input,
      { connectedAccountId: storedAccount.connectedAccountId },
    ),
};
```

调用 handler 前，按 schema 校验工具参数。模型只提供搜索条件。先开放产品需要的操作，再按用户已连接的服务筛选；SDK 不会自动把所有 provider 操作注册为 Agent 工具。

## 明确产品边界

你的产品负责鉴权自己的用户，并控制用户可以使用哪些 providers 与 actions。Project API key 只保存在后端；始终传入一致的 `externalUserId`，并把返回的账号选择器保存到对应业务用户下。

[TypeScript SDK 参考](/zh-cn/docs/connector-sdk/)包含授权请求与 connected account 的生命周期字段、精确 action 类型、错误、重试、等待选项和完整 `ProjectConnector` API。
