@super_studio/ecforce-ai-agent-server 1.4.0-canary.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -2
- package/dist/mcp-auth.d.cts +1 -1
- package/dist/mcp-auth.d.mts +1 -1
- package/dist/mcp-auth.mjs.map +1 -1
- package/dist/sdk/__generated__/index.cjs +64 -7
- package/dist/sdk/__generated__/index.d.cts +183 -19
- package/dist/sdk/__generated__/index.d.cts.map +1 -1
- package/dist/sdk/__generated__/index.d.mts +183 -19
- package/dist/sdk/__generated__/index.d.mts.map +1 -1
- package/dist/sdk/__generated__/index.mjs +64 -7
- package/dist/sdk/__generated__/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/mcp-auth.ts +1 -1
- package/src/sdk/__generated__/index.ts +449 -64
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@super_studio/ecforce-ai-agent-server`
|
|
2
2
|
|
|
3
|
-
ecforce AI
|
|
3
|
+
ecforce AIエージェント のサーバー向け SDK です。
|
|
4
4
|
|
|
5
5
|
主に次の 2 つを提供します。
|
|
6
6
|
|
|
@@ -56,7 +56,7 @@ AI_AGENT_API_ENDPOINT=https://dev-agent.ec-force.com
|
|
|
56
56
|
|
|
57
57
|
### `spst()`
|
|
58
58
|
|
|
59
|
-
Vercel AI SDK の `generateText()` / `streamText()` などから、ecforce AI
|
|
59
|
+
Vercel AI SDK の `generateText()` / `streamText()` などから、ecforce AIエージェント の proxy provider を使いたいときに使います。
|
|
60
60
|
|
|
61
61
|
用途:
|
|
62
62
|
|
|
@@ -119,6 +119,47 @@ type AgentSession = {
|
|
|
119
119
|
};
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### 外部アプリで利用規約の同意を管理する
|
|
123
|
+
|
|
124
|
+
利用規約は `https://agent.ec-force.com/terms` で公開されています。外部アプリでは、このページへのリンクを表示してから同意を記録してください。
|
|
125
|
+
|
|
126
|
+
DB に登録済みのユーザーは、Accounts API を呼ばずに同意状況だけ取得できます。
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const terms = await client.internalTerms.status({
|
|
130
|
+
email: "user@example.com",
|
|
131
|
+
projectId: "project_123",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!terms.accepted) {
|
|
135
|
+
// 利用規約へのリンクと同意チェックボックスを表示
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
ユーザーが同意したら、次の API で記録します。未登録ユーザーも同時に作成する場合だけ `createIfMissing: true` を指定してください。
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
await client.internalTerms.accept({
|
|
143
|
+
email: "user@example.com",
|
|
144
|
+
projectId: "project_123",
|
|
145
|
+
createIfMissing: true,
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
ユーザー情報も必要な場合は `resolve` を使います。`createIfMissing` の既定値は `false` です。
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const user = await client.internalUser.resolve({
|
|
153
|
+
email: "user@example.com",
|
|
154
|
+
projectId: "project_123",
|
|
155
|
+
createIfMissing: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
console.log(user.role, user.plan, user.termsAcceptedAt);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
存在しないユーザーに `createIfMissing: false` でアクセスすると、`NOT_FOUND` エラーになります。ユーザー作成時は既存のルールに従い、有料プロジェクトでは Accounts に登録済みのメールアドレスだけ作成できます。
|
|
162
|
+
|
|
122
163
|
### 内部 usage summary を取得する
|
|
123
164
|
|
|
124
165
|
内部 API の利用例として、`/v1/internal/usage/summary` に対応する usage summary 取得は次のように呼べます。
|
|
@@ -266,6 +307,114 @@ const result = streamText({
|
|
|
266
307
|
});
|
|
267
308
|
```
|
|
268
309
|
|
|
310
|
+
### エラーハンドリング
|
|
311
|
+
|
|
312
|
+
ユーザーが未登録、または利用規約に未同意の場合、プロキシは判別可能なエラーコードを返します。
|
|
313
|
+
|
|
314
|
+
| HTTP | `error.code` | 意味 |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| 404 | `user_not_found` | 指定したプロジェクトにユーザーが存在しない |
|
|
317
|
+
| 403 | `terms_not_accepted` | ユーザーが利用規約に同意していない |
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { APICallError } from "ai";
|
|
321
|
+
|
|
322
|
+
function isTermsNotAcceptedError(error: unknown) {
|
|
323
|
+
return (
|
|
324
|
+
APICallError.isInstance(error) &&
|
|
325
|
+
error.statusCode === 403 &&
|
|
326
|
+
error.responseBody?.includes("terms_not_accepted") === true
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
クレジット上限に達すると、プロキシは **429** を返します。
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"error": {
|
|
336
|
+
"message": "Credit limit exceeded.",
|
|
337
|
+
"type": "rate_limit_error",
|
|
338
|
+
"code": "credit_limit_exceeded"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Vercel AI SDK では、このエラーは `APICallError` として扱われます。`generateText` と `streamText` で受け取り方が異なる点に注意してください。
|
|
344
|
+
|
|
345
|
+
#### `generateText`
|
|
346
|
+
|
|
347
|
+
`generateText()` 自体が throw します。
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
import { APICallError, generateText } from "ai";
|
|
351
|
+
import { createSpstProvider } from "@super_studio/ecforce-ai-agent-server/provider";
|
|
352
|
+
|
|
353
|
+
const provider = createSpstProvider({ callerApp: "admin-dashboard" });
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
await generateText({
|
|
357
|
+
model: provider("openai/gpt-5.4"),
|
|
358
|
+
prompt: "売上の改善案を3つ提案して",
|
|
359
|
+
maxRetries: 0,
|
|
360
|
+
providerOptions: {
|
|
361
|
+
spst: {
|
|
362
|
+
projectId: "project_123",
|
|
363
|
+
email: "user@example.com",
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (isCreditLimitError(error)) {
|
|
369
|
+
// 例: UI に「クレジット上限に達しました」を表示
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function isCreditLimitError(error: unknown) {
|
|
377
|
+
return (
|
|
378
|
+
APICallError.isInstance(error) &&
|
|
379
|
+
error.statusCode === 429 &&
|
|
380
|
+
error.responseBody?.includes("credit_limit_exceeded") === true
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
#### `streamText`
|
|
386
|
+
|
|
387
|
+
`streamText()` の HTTP エラーは `onError` で受け取ります。`await result.text` 側で credit limit を判定する必要はありません。
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import { APICallError, streamText } from "ai";
|
|
391
|
+
import { createSpstProvider } from "@super_studio/ecforce-ai-agent-server/provider";
|
|
392
|
+
|
|
393
|
+
const provider = createSpstProvider({ callerApp: "admin-dashboard" });
|
|
394
|
+
|
|
395
|
+
const result = streamText({
|
|
396
|
+
model: provider("openai/gpt-5.4-mini"),
|
|
397
|
+
prompt: "FAQ の草案を作成して",
|
|
398
|
+
maxRetries: 0,
|
|
399
|
+
providerOptions: {
|
|
400
|
+
spst: {
|
|
401
|
+
projectId: "project_123",
|
|
402
|
+
email: "user@example.com",
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
onError: ({ error }) => {
|
|
406
|
+
if (isCreditLimitError(error)) {
|
|
407
|
+
// 例: UI に「クレジット上限に達しました」を表示
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
console.error(error);
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
return result.toUIMessageStreamResponse();
|
|
416
|
+
```
|
|
417
|
+
|
|
269
418
|
### Google Search tool を併用する
|
|
270
419
|
|
|
271
420
|
Google 系モデルでは、AI SDK 側のツールを組み合わせて使えます。
|
package/dist/mcp-auth.d.cts
CHANGED
package/dist/mcp-auth.d.mts
CHANGED
package/dist/mcp-auth.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-auth.mjs","names":[],"sources":["../src/mcp-auth.ts"],"sourcesContent":["import { decode, encode } from \"./lib/jwt\";\n\n/**\n * 共通のMCPトークンのペイロード。\n * すべてのapps(
|
|
1
|
+
{"version":3,"file":"mcp-auth.mjs","names":[],"sources":["../src/mcp-auth.ts"],"sourcesContent":["import { decode, encode } from \"./lib/jwt\";\n\n/**\n * 共通のMCPトークンのペイロード。\n * すべてのapps(aidp, ma, bi)はこのトークンの形になります。\n */\nexport type MCPTokenPayload = {\n source: string;\n user: {\n id: string;\n name: string;\n email: string;\n projectId: string;\n };\n};\n\nconst DEFAULT_MAX_AGE = 60 * 10; // 10 minutes\nconst DEFAULT_SALT = process.env.STAGE === \"local\" ? \"dev\" : process.env.STAGE;\n\n/**\n * Uses Auth.js JWT encoding/decoding.\n * @see https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/jwt.ts\n */\nexport async function createMCPToken(\n payload: MCPTokenPayload,\n options?: {\n secret?: string;\n salt?: string;\n maxAge?: number;\n },\n) {\n const maxAge = options?.maxAge ?? DEFAULT_MAX_AGE;\n const secret = options?.secret ?? process.env.MCP_TOKEN_SECRET;\n const salt = options?.salt ?? DEFAULT_SALT;\n if (!secret || !salt) {\n throw new Error(\"Secret or salt is not set\");\n }\n const token = await encode<MCPTokenPayload>({\n token: payload,\n secret,\n salt,\n maxAge,\n });\n return {\n token,\n expiresAt: new Date(Date.now() + maxAge * 1000),\n };\n}\n\n/**\n * Uses Auth.js JWT encoding/decoding.\n * @see https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/jwt.ts\n */\nexport async function decodeMCPToken(\n token: string,\n options?: {\n secret?: string;\n salt?: string;\n },\n): Promise<MCPTokenPayload> {\n const secret = options?.secret ?? process.env.MCP_TOKEN_SECRET;\n const salt = options?.salt ?? DEFAULT_SALT;\n if (!secret || !salt) {\n throw new Error(\"Secret or salt is not set\");\n }\n const decoded = await decode<MCPTokenPayload>({\n token,\n secret,\n salt,\n });\n if (!decoded) {\n throw new Error(\"Invalid token\");\n }\n return decoded;\n}\n\nexport function getMcpToken(req: Request) {\n const mcpToken = req.headers.get(\"X-MCP-Token\");\n if (!mcpToken || typeof mcpToken !== \"string\") {\n throw new Error(\"Unauthorized\");\n }\n return mcpToken;\n}\n"],"mappings":";;;AAgBA,MAAM,kBAAkB;AACxB,MAAM,eAAe,QAAQ,IAAI,UAAU,UAAU,QAAQ,QAAQ,IAAI;;;;;AAMzE,eAAsB,eACpB,SACA,SAKA;;CACA,MAAM,8EAAS,QAAS,mEAAU;CAClC,MAAM,8EAAS,QAAS,mEAAU,QAAQ,IAAI;CAC9C,MAAM,0EAAO,QAAS,6DAAQ;AAC9B,KAAI,CAAC,UAAU,CAAC,KACd,OAAM,IAAI,MAAM,4BAA4B;AAQ9C,QAAO;EACL,OAPY,MAAM,OAAwB;GAC1C,OAAO;GACP;GACA;GACA;GACD,CAAC;EAGA,WAAW,IAAI,KAAK,KAAK,KAAK,GAAG,SAAS,IAAK;EAChD;;;;;;AAOH,eAAsB,eACpB,OACA,SAI0B;;CAC1B,MAAM,+EAAS,QAAS,qEAAU,QAAQ,IAAI;CAC9C,MAAM,2EAAO,QAAS,+DAAQ;AAC9B,KAAI,CAAC,UAAU,CAAC,KACd,OAAM,IAAI,MAAM,4BAA4B;CAE9C,MAAM,UAAU,MAAM,OAAwB;EAC5C;EACA;EACA;EACD,CAAC;AACF,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,gBAAgB;AAElC,QAAO;;AAGT,SAAgB,YAAY,KAAc;CACxC,MAAM,WAAW,IAAI,QAAQ,IAAI,cAAc;AAC/C,KAAI,CAAC,YAAY,OAAO,aAAa,SACnC,OAAM,IAAI,MAAM,eAAe;AAEjC,QAAO"}
|
|
@@ -262,13 +262,41 @@ var Api = class extends HttpClient {
|
|
|
262
262
|
format: "json"
|
|
263
263
|
}, params))
|
|
264
264
|
};
|
|
265
|
-
this.internalUser = {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
this.internalUser = {
|
|
266
|
+
list: (query, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
267
|
+
path: `/v1/internal/user`,
|
|
268
|
+
method: "GET",
|
|
269
|
+
query,
|
|
270
|
+
secure: true,
|
|
271
|
+
format: "json"
|
|
272
|
+
}, params)),
|
|
273
|
+
resolve: (data, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
274
|
+
path: `/v1/internal/user/resolve`,
|
|
275
|
+
method: "POST",
|
|
276
|
+
body: data,
|
|
277
|
+
secure: true,
|
|
278
|
+
type: ContentType.Json,
|
|
279
|
+
format: "json"
|
|
280
|
+
}, params))
|
|
281
|
+
};
|
|
282
|
+
this.internalTerms = {
|
|
283
|
+
status: (data, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
284
|
+
path: `/v1/internal/terms/status`,
|
|
285
|
+
method: "POST",
|
|
286
|
+
body: data,
|
|
287
|
+
secure: true,
|
|
288
|
+
type: ContentType.Json,
|
|
289
|
+
format: "json"
|
|
290
|
+
}, params)),
|
|
291
|
+
accept: (data, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
292
|
+
path: `/v1/internal/terms/accept`,
|
|
293
|
+
method: "POST",
|
|
294
|
+
body: data,
|
|
295
|
+
secure: true,
|
|
296
|
+
type: ContentType.Json,
|
|
297
|
+
format: "json"
|
|
298
|
+
}, params))
|
|
299
|
+
};
|
|
272
300
|
this.internalGlobalAgent = {
|
|
273
301
|
listGlobalAgents: (params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
274
302
|
path: `/v1/internal/global-agent`,
|
|
@@ -705,6 +733,29 @@ var Api = class extends HttpClient {
|
|
|
705
733
|
format: "json"
|
|
706
734
|
}, params))
|
|
707
735
|
};
|
|
736
|
+
this.mcpOauth = {
|
|
737
|
+
getGrants: (query, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
738
|
+
path: `/v1/mcp-oauth/grants`,
|
|
739
|
+
method: "GET",
|
|
740
|
+
query,
|
|
741
|
+
secure: true,
|
|
742
|
+
format: "json"
|
|
743
|
+
}, params)),
|
|
744
|
+
postGrantsRevokeAll: (params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
745
|
+
path: `/v1/mcp-oauth/grants/revoke-all`,
|
|
746
|
+
method: "POST",
|
|
747
|
+
secure: true,
|
|
748
|
+
format: "json"
|
|
749
|
+
}, params)),
|
|
750
|
+
"postGrants{id}Revoke": (id, data, params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
751
|
+
path: `/v1/mcp-oauth/grants/${id}/revoke`,
|
|
752
|
+
method: "POST",
|
|
753
|
+
body: data,
|
|
754
|
+
secure: true,
|
|
755
|
+
type: ContentType.Json,
|
|
756
|
+
format: "json"
|
|
757
|
+
}, params))
|
|
758
|
+
};
|
|
708
759
|
this.savedPrompts = {
|
|
709
760
|
list: (params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
710
761
|
path: `/v1/saved-prompts`,
|
|
@@ -755,6 +806,12 @@ var Api = class extends HttpClient {
|
|
|
755
806
|
method: "POST",
|
|
756
807
|
secure: true,
|
|
757
808
|
format: "json"
|
|
809
|
+
}, params)),
|
|
810
|
+
createEmbedToken: (params = {}) => this.request(require_objectSpread2._objectSpread2({
|
|
811
|
+
path: `/v1/session/embed-token`,
|
|
812
|
+
method: "POST",
|
|
813
|
+
secure: true,
|
|
814
|
+
format: "json"
|
|
758
815
|
}, params))
|
|
759
816
|
};
|
|
760
817
|
this.usage = {
|