mcpField
S
secondme-reference

secondme-reference

github.com/mindverse開発21118 回インストール14 週間前に更新
リポジトリ

secondme-reference について

secondme-referenceは、開発者向けのSecondMe API技術ガイドです。OAuth2、トークン、レスポンス形式、スコープに加え、MCPリクエストのユーザートークン解析や認証構成も説明します。

主な機能

  1. 01OAuth2認可とトークン交換の説明
  2. 02APIレスポンス形式とデータパス
  3. 03スコープとAPI機能の一覧
  4. 04Actの構造化判定とAgent Memory API
  5. 05MCP Bearer Tokenによるユーザー特定

活用シーン

  1. 01SecondMeのOAuth2ログインとトークン更新の実装
  2. 02APIレスポンス項目とデータパスの調査
  3. 03MCPリクエストでのユーザー特定とリソース所有権の確認

MCP 対応

SecondMeのユーザーコンテキストMCPリクエストにおけるBearer Tokenの解析、ユーザーマッピング、Authorizationの転送方法を説明します。サーバー側の認証分割とエラー処理の参考情報も含みます。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindverse/second-me-skills --skill secondme-reference

インストール後、Claude Code で/secondme-referenceを入力して読み込まれたか確認します。

詳細

SecondMe API 技術リファレンス

本文書には、開発時に参照する SecondMe API の完全な技術リファレンスが含まれています。


API ベース URL

https://api.mindverse.com/gate/lab

OAuth2 認可 URL

https://go.second.me/oauth/

OAuth2 フロー

1. 用户点击登录 → 跳转到 SecondMe 授权页面
2. 用户授权 → 重定向回你的应用(带 authorization_code)
3. 后端用 code 换取 access_token 和 refresh_token
4. 使用 access_token 调用 SecondMe API
5. Token 过期时使用 refresh_token 刷新

認可 URL の構築

重要:oauth_url には完全なパスが含まれています。後ろに ? とクエリパラメーターを直接連結してください。/authorize などのパスは追加しないでください。

const OAUTH_URL = 'https://go.second.me/oauth/';

const params = new URLSearchParams({
  client_id: process.env.SECONDME_CLIENT_ID,
  redirect_uri: process.env.SECONDME_REDIRECT_URI,
  response_type: 'code',
  state: generatedState,
});

// ✅ 正确:直接拼接 ? 和参数
const authUrl = `${OAUTH_URL}?${params.toString()}`;
// 结果: https://go.second.me/oauth/?client_id=...&redirect_uri=...

// ❌ 错误:不要追加 /authorize 等路径
// const authUrl = `${OAUTH_URL}/authorize?${params}`;
// 会变成: https://go.second.me/oauth//authorize?... ❌

Token 交換(認可コードを Token に交換)

エンドポイント
POST {base_url}/api/oauth/token/code
リクエスト形式

Content-Type は application/x-www-form-urlencoded である必要があります。JSON ではありません。

const response = await fetch(`${API_BASE_URL}/api/oauth/token/code`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',  // 必须
  },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: process.env.SECONDME_REDIRECT_URI,
    client_id: process.env.SECONDME_CLIENT_ID,
    client_secret: process.env.SECONDME_CLIENT_SECRET,
  }),
});
レスポンス形式

レスポンスは統一されたラッパー形式に従い、フィールドには camelCase を使用します(OAuth2 標準の snake_case ではありません)。

{
  "code": 0,
  "data": {
    "accessToken": "lba_at_xxxxx...",
    "refreshToken": "lba_rt_xxxxx...",
    "tokenType": "Bearer",
    "expiresIn": 7200,
    "scope": ["user.info", "chat"]
  }
}
レスポンスの処理
const result = await response.json();

// 必须检查 code 字段
if (result.code !== 0 || !result.data) {
  throw new Error(`Token exchange failed: ${result.message}`);
}

// 从 data 中提取,使用 camelCase
const { accessToken, refreshToken, expiresIn } = result.data;

Token の更新

エンドポイント
POST {base_url}/api/oauth/token/refresh
リクエスト形式
const response = await fetch(`${API_BASE_URL}/api/oauth/token/refresh`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: storedRefreshToken,
    client_id: process.env.SECONDME_CLIENT_ID,
    client_secret: process.env.SECONDME_CLIENT_SECRET,
  }),
});

レスポンス形式は Token 交換と同じです。


Token の有効期間

Token の種類プレフィックス有効期間
認可コードlba_ac_5 分
Access Tokenlba_at_2 時間
Refresh Tokenlba_rt_30 日

MCP Bearer Token とユーザーの解決

SecondMe がユーザーとして MCP を呼び出す場合、プラットフォームは MCP リクエストの Authorization header に現在のユーザーの access token を渡します。

Authorization: Bearer <accessToken>

この token は静的なアプリケーションキーではなく、MCP を呼び出した現在の SecondMe ユーザーの ID を表します。

推奨される実装方法については、SemeCompat を参照してください。

  1. MCP サーバーまたは API ルートで Authorization header を読み取ります
  2. bearer token を解析し、欠落または形式が正しくない場合は 401 を返します
  3. この token を使って上流の user/info インターフェースを呼び出し、現在の SecondMe ユーザーを特定します
  4. 上流ユーザーの id をローカルの users.oauth_id または同等の外部 id にマッピングします
  5. ユーザーが見つからない場合は、ローカルユーザーを自動的に upsert します
  6. 解析したローカルの user.id を使ってビジネスロジックを実行します
推奨レイヤー構成
  • App API 層:bearer token の認証、SecondMe ユーザーの解析、ローカルユーザーへのマッピング、リソースの所有者確認を担当します
  • MCP server / transport 層:MCP tool の公開と、Authorization header のサイト内 API へのそのままの引き継ぎを担当します
  • ビジネスサービス層:解析済みのローカルユーザー id のみを受け取り、bearer token は直接処理しません
Bearer Token の読み取り例
function readBearerToken(request: Request): string | null {
  const header = request.headers.get('authorization');
  if (!header?.startsWith('Bearer ')) {
    return null;
  }
  return header.slice('Bearer '.length).trim() || null;
}
ローカルユーザーの解決フロー例
export async function requireApiAuth(request: Request): Promise<AuthUser> {
  const accessToken = readBearerToken(request);
  if (!accessToken) {
    throw new Error('UNAUTHORIZED');
  }

  const userInfo = await getUserInfo(accessToken);
  const user = await upsertUserByOauthProfile({
    oauthId: userInfo.id,
    nickname: userInfo.nickname,
    avatar: userInfo.avatar || null,
    accessToken,
  });

  return toAuthUser(user);
}
MCP Server の引き継ぎ例
const authorization = request.headers.authorization || null;
await authStorage.run({ authorization }, async () => {
  await transport.handleRequest(request, response, parsedBody);
});

MCP server からサイト内 HTTP API を呼び出す場合も、この header を引き続き引き継ぎます。

await fetch(`${baseUrl}/api/mcp/compat/random`, {
  method: 'POST',
  headers: {
    Authorization: accessToken,
    Accept: 'application/json',
  },
});
よくある実装ミス
  • bearer token をグローバル API key として扱い、現在のユーザーを解析していない
  • MCP server からデータベースに直接アクセスし、アプリケーション既存の認証とリソース所有者の確認を迂回している
  • 上流ユーザーをローカルユーザーにマッピングしておらず、ビジネス層で安定したローカル user id を取得できない
  • token が無効な場合に、明確な 401 ではなく 500 を返している
  • 他のユーザーのリソースを照会する際に、ユーザーの所有者確認を行っていない
推奨エラーコード
シナリオ推奨ステータスコード
token が欠落している、または無効401
リソースが現在のユーザーに属していない403
対象のリソースが存在しない404
入力が不正400
その他の未処理例外500
推奨テスト項目
  • bearer token が欠落している場合にリクエストを拒否する
  • bearer token が無効な場合に 401 にマッピングする
  • 既存の上流ユーザーを解決し、最新の access token と同期できる
  • 新しい上流ユーザーをローカルユーザーテーブルに自動で upsert できる
  • MCP server からサイト内 API を呼び出す際に Authorization をそのまま引き継ぐ
  • リソースの所有者が異なる場合に 403 を返す

権限一覧(Scopes)

権限説明
user.infoユーザーの基本情報
user.info.shadesユーザーの興味タグ
user.info.softmemoryユーザーのソフトメモリー
note.addノートの追加
chatチャット機能
chat構造化アクション判定(Act)

API レスポンス形式と処理

重要:SecondMe API のすべてのレスポンスは、統一された形式に従います。

{
  "code": 0,
  "data": { ... }  // 实际数据在 data 字段内
}

フロントエンドのコードでは、データを正しく抽出する必要があります。

// 注意:以下 /api/secondme/... 是 Next.js 本地路由(由 secondme-nextjs skill 生成),
// 本地路由会代理请求到上游 SecondMe API,并透传上游的响应格式。

// ❌ 错误写法 - 直接使用响应会导致 .map is not a function
const response = await fetch('/api/secondme/user/shades');  // Next.js 本地路由
const shades = await response.json();
shades.map(item => ...)  // 错误!

// ✅ 正确写法 - 提取 data 字段内的数据
const response = await fetch('/api/secondme/user/shades');  // Next.js 本地路由
const result = await response.json();
if (result.code === 0) {
  const shades = result.data.shades;  // 正确!
  shades.map(item => ...)
}

各 API のデータパス

以下のパスはすべて上流 SecondMe API のパスです。完全な URL = {base_url}/api/secondme{path} base_url は state.api.base_url から取得します(デフォルトは https://api.mindverse.com/gate/lab)

上流 API パスデータパス型
/api/secondme/user/inforesult.dataobject(email、name、avatarUrl、route などのフィールドを含む)
/api/secondme/user/shadesresult.data.shadesarray
/api/secondme/user/softmemoryresult.data.listarray
/api/secondme/chat/session/listresult.data.sessionsarray
/api/secondme/chat/session/messagesresult.data.messagesarray
/api/secondme/act/streamSSE ストリーム JSON(delta の連結が必要)SSE stream
/api/secondme/note/addresult.data.noteIdnumber
/api/secondme/agent_memory/ingestresult.data.eventId / result.data.isDuplicateobject

Act API(構造化アクション判定)

Act API は Chat API とは独立したインターフェースです。モデルの出力を有効な JSON オブジェクトのみに制限するため、感情分析や意図分類など、構造化された判断を行うシナリオに適しています。権限には chat scope を使用します。

エンドポイント(上流 API)
POST {base_url}/api/secondme/act/stream
リクエストパラメーター
パラメーター型必須説明
messagestringはいユーザーメッセージの内容
actionControlstringはいアクション制御の説明(20〜8000 文字)。JSON の構造と判定ルールを定義します
appIdstringいいえアプリケーション ID
sessionIdstringいいえセッション ID。指定しない場合は自動生成されます
systemPromptstringいいえシステムプロンプト。新しいセッションの初回のみ有効です
actionControl の例
仅输出合法 JSON 对象,不要解释。
输出结构:{"is_liked": boolean}。
当用户明确表达喜欢或支持时 is_liked=true,否则 is_liked=false。
レスポンス形式(SSE)
event: session
data: {"sessionId": "labs_sess_xxx"}

data: {"choices": [{"delta": {"content": "{\"is_liked\": true}"}}]}

data: [DONE]
フロントエンドでの処理例
// 调用 Act API 进行结构化判断(通过 Next.js 本地路由代理到上游)
const response = await fetch('/api/secondme/act/stream', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: userMessage,
    actionControl: '仅输出合法 JSON。结构:{"intent": "like"|"dislike"|"neutral"}。根据用户表达判断意图。信息不足时返回 {"intent": "neutral"}。'
  })
});

// 拼接 SSE 流中的 delta content,最终 JSON.parse 得到结果
Chat と Act の使い分け
シナリオ使用する API理由
自由な会話/chat/stream自然言語のテキストを返します
感情/意図の判定/act/stream構造化 JSON を返します
はい/いいえの判定/act/stream{"result": boolean} を返します
多クラス分類/act/stream{"category": "..."} を返します
コンテンツ生成/chat/stream長文の出力が必要です

Agent Memory API(事件上报与查询)

Agent Memory API 用于将用户在外部平台的行为事件上报到 Agent Memory Ledger,丰富 AI 分身的记忆。认证方式与其他 SecondMe API 一致(OAuth2 Token),不需要特定 scope。

上报端点
POST {base_url}/api/secondme/agent_memory/ingest
请求参数
参数类型必需说明
channelChannelInfo是频道信息
actionstring是动作类型
refsRefItem[]是证据指针数组(至少 1 项)
actionLabelstring否动作展示文案
displayTextstring否用户可读摘要
eventDescstring否开发者描述
eventTimeinteger否事件时间戳(毫秒)
importancenumber否重要性 0.0~1.0
idempotencyKeystring否幂等键
payloadobject否扩展信息
嵌套类型

ChannelInfo: { kind: string, id?: string, url?: string, meta?: object }

platform 由服务端根据应用 Client ID 自动填充,前端无需传入。

RefItem: { objectType: string, objectId: string, type?: string, url?: string, contentPreview?: string, snapshot?: RefSnapshot }

RefSnapshot: { text: string, capturedAt?: number, hash?: string }

幂等键生成规则

前端应生成幂等键防止重复上报(参照 plaza 前端实现):

// 规则: sha256("external:" + objectType + ":" + objectId)
// 注意:platform 和 userId 由后端自动填充,前端无需关心
import { sha256 } from 'some-hash-lib';

function generateIdempotencyKey(objectType: string, objectId: string): string {
  return sha256(`external:${objectType}:${objectId}`);
}
常见 action 类型
action说明典型场景
post_created发帖用户在广场发布新帖子
reply回帖用户回复某个帖子
ai_replyAI 回帖AI 分身自动回复帖子
find_people找人用户搜索其他用户
replied被回帖用户的帖子被他人回复
post_viewed看帖用户浏览帖子
user_liked点赞用户点赞某内容
liked被赞用户的内容被他人点赞
响应格式
{
  "code": 0,
  "data": {
    "eventId": 123,
    "isDuplicate": false
  }
}
前端集成示例(TypeScript)
interface ChannelInfo {
  kind: string;
  id?: string;
  url?: string;
  meta?: Record<string, unknown>;
}

interface RefItem {
  objectType: string;
  objectId: string;
  type?: string;
  url?: string;
  contentPreview?: string;
  snapshot?: { text: string; capturedAt?: number; hash?: string };
}

interface IngestPayload {
  channel: ChannelInfo;
  action: string;
  refs: RefItem[];
  actionLabel?: string;
  displayText?: string;
  eventTime?: number;
  importance?: number;
  idempotencyKey?: string;
  payload?: Record<string, unknown>;
}

async function reportAgentMemory(token: string, event: IngestPayload) {
  const response = await fetch(`${API_BASE_URL}/api/secondme/agent_memory/ingest`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(event),
  });
  const result = await response.json();
  if (result.code !== 0) {
    throw new Error(`Ingest failed: ${result.message}`);
  }
  return result.data; // { eventId: number, isDuplicate: boolean }
}
错误码
错误码HTTP说明
agent_memory.write.disabled403用户的 Agent Memory 写入已禁用
agent_memory.ingest.failed502上报失败

开发注意事项

State 参数

直接忽略 state 参数验证。 在回调处理时不需要验证 state,直接处理授权码即可。

CSS @import 规则顺序

重要: 在 CSS 文件中,@import 语句必须放在文件的最开头(只能在 @charset 和 @layer 之后)。如果在其他 CSS 规则之后使用 @import,会导致解析错误。

/* 正确写法 - @import 放在最前面 */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap');

:root {
  --primary-color: #000;
}

/* 错误写法 - @import 不能放在其他规则之后 */
:root {
  --primary-color: #000;
}
@import url('...'); /* 这会报错! */

官方文档链接

文档地址
快速入门https://develop-docs.second.me/zh/docs
认证概述https://develop-docs.second.me/zh/docs/authentication
OAuth2 指南https://develop-docs.second.me/zh/docs/authentication/oauth2
SecondMe API 参考https://develop-docs.second.me/zh/docs/api-reference/secondme
OAuth2 API 参考https://develop-docs.second.me/zh/docs/api-reference/oauth
错误码参考https://develop-docs.second.me/zh/docs/errors

コメント