@knownagents/sdk 2.0.0 → 2.2.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 +55 -104
- package/dist/KnownAgents.d.ts +23 -8
- package/dist/KnownAgents.d.ts.map +1 -1
- package/dist/KnownAgents.js +73 -27
- package/dist/headers.d.ts +1 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +15 -34
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/KnownAgents.ts +97 -80
- package/src/headers.ts +2 -2
- package/src/index.ts +1 -2
- package/src/types.ts +15 -35
- package/dist/commerce.d.ts +0 -28
- package/dist/commerce.d.ts.map +0 -1
- package/dist/commerce.js +0 -51
- package/src/commerce.ts +0 -68
package/README.md
CHANGED
|
@@ -22,131 +22,92 @@ import { KnownAgents } from "@knownagents/sdk"
|
|
|
22
22
|
const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN")
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Get realtime insight into the hidden ecosystem of [AI agents and other bots](https://knownagents.com/agents) browsing your website, REST APIs, and [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) servers. Measure human traffic coming from AI chat and search platforms like ChatGPT, Perplexity, and Gemini.
|
|
28
|
-
|
|
29
|
-
### Track Pageview and REST Calls
|
|
30
|
-
|
|
31
|
-
To collect pageview and REST API analytics, call `trackPageviewOrRESTCall` with the incoming Node.js request and response.
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
knownAgents.trackPageviewOrRESTCall(request, response)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
For REST APIs that return [ACP (Agentic Commerce Protocol)](https://www.agenticcommerce.dev/) or [UCP (Universal Commerce Protocol)](https://ucp.dev/) commerce metadata, include the response body:
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
knownAgents.trackPageviewOrRESTCall(request, response, {
|
|
41
|
-
restACPResponseBody: acpResponseBody
|
|
42
|
-
})
|
|
43
|
-
```
|
|
25
|
+
To batch visits, set the visit event queue size and flush interval:
|
|
44
26
|
|
|
45
27
|
```ts
|
|
46
|
-
knownAgents
|
|
47
|
-
|
|
28
|
+
const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN", {
|
|
29
|
+
flushQueueSize: 1000,
|
|
30
|
+
flushIntervalInMilliseconds: 10000
|
|
48
31
|
})
|
|
49
32
|
```
|
|
50
33
|
|
|
51
|
-
The
|
|
34
|
+
The entire visit event queue is uploaded when it reaches `flushQueueSize` or when the flush interval elapses after the first visit event enters an empty queue.
|
|
52
35
|
|
|
53
|
-
|
|
36
|
+
## Send Visit Events ([Agent Analytics](https://knownagents.com/products/agent-analytics), [LLM Referral Tracking](https://knownagents.com/products/llm-referral-tracking))
|
|
54
37
|
|
|
55
|
-
|
|
38
|
+
Get realtime insight into how [AI agents and other bots](https://knownagents.com/agents) browse your web pages, call your [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) endpoints, and purchase products through your [ACP (Agentic Commerce Protocol)](https://www.agenticcommerce.dev/) and [UCP (Universal Commerce Protocol)](https://ucp.dev/) shopping flows. Measure human traffic from AI chat and search platforms like ChatGPT, Claude, and Gemini.
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
import express from "express"
|
|
59
|
-
import { KnownAgents } from "@knownagents/sdk"
|
|
40
|
+
### Pageviews and REST Calls
|
|
60
41
|
|
|
61
|
-
|
|
62
|
-
const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN")
|
|
42
|
+
Call `trackPageviewOrRESTCall` with the incoming request and outgoing response. If you can, do this in middleware. This method skips MCP calls automatically.
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
next()
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
app.get("/", (request, response) => {
|
|
70
|
-
response.send("Hello, world!")
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
app.listen(3000, () => console.log("Server running on port 3000"))
|
|
44
|
+
```ts
|
|
45
|
+
knownAgents.trackPageviewOrRESTCall(request, response)
|
|
74
46
|
```
|
|
75
47
|
|
|
76
|
-
###
|
|
77
|
-
|
|
78
|
-
For [MCP](https://modelcontextprotocol.io/) servers that use `StreamableHTTPServerTransport`, call `trackMCPCall` after connecting the transport and before the transport handles the request.
|
|
79
|
-
|
|
80
|
-
For MCP calls that return [ACP](https://www.agenticcommerce.dev/) or [UCP](https://ucp.dev/) commerce metadata, the SDK captures the relevant currency and total amount automatically.
|
|
48
|
+
### MCP Calls (Optional)
|
|
81
49
|
|
|
82
|
-
|
|
50
|
+
For MCP servers that use `StreamableHTTPServerTransport`, call `trackMCPCall` after connecting the transport and before the transport handles the request.
|
|
83
51
|
|
|
84
52
|
```ts
|
|
53
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
|
|
85
54
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
|
|
86
55
|
|
|
56
|
+
// ...
|
|
57
|
+
|
|
58
|
+
const mcpServer = new McpServer({
|
|
59
|
+
name: SERVER_NAME,
|
|
60
|
+
version: SERVER_VERSION,
|
|
61
|
+
})
|
|
62
|
+
|
|
87
63
|
const transport = new StreamableHTTPServerTransport({
|
|
88
64
|
sessionIdGenerator: undefined
|
|
89
65
|
})
|
|
90
66
|
|
|
67
|
+
// ...
|
|
68
|
+
|
|
91
69
|
await mcpServer.connect(transport)
|
|
70
|
+
|
|
92
71
|
knownAgents.trackMCPCall(request, response, transport)
|
|
72
|
+
|
|
93
73
|
await transport.handleRequest(request, response, parsedBody)
|
|
94
74
|
```
|
|
95
75
|
|
|
96
|
-
|
|
76
|
+
### Agentic Commerce Interactions (Optional)
|
|
97
77
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
For high-traffic websites, batch multiple visits together and send them periodically (e.g. every 30 seconds) using `trackVisits`. `trackVisits` can also batch MCP visits; if you do that, include the MCP fields in each `VisitRequest` and do not also call `trackMCPCall` for the same requests. Here's an Express example:
|
|
78
|
+
For REST implementations of ACP and UCP, include the response body in `trackPageviewOrRESTCall`.
|
|
101
79
|
|
|
102
80
|
```ts
|
|
103
|
-
|
|
104
|
-
import { KnownAgents, type VisitRequest } from "@knownagents/sdk"
|
|
81
|
+
// For ACP calls ...
|
|
105
82
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const visits: VisitRequest[] = []
|
|
109
|
-
|
|
110
|
-
app.use((request, response, next) => {
|
|
111
|
-
const created = new Date()
|
|
112
|
-
|
|
113
|
-
response.once("finish", () => {
|
|
114
|
-
visits.push({
|
|
115
|
-
request_path: request.url,
|
|
116
|
-
request_method: request.method,
|
|
117
|
-
request_headers: request.headers,
|
|
118
|
-
response_status_code: response.statusCode,
|
|
119
|
-
response_headers: response.getHeaders(),
|
|
120
|
-
response_duration_in_milliseconds: Date.now() - created.getTime(),
|
|
121
|
-
created: created.toISOString()
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
next()
|
|
83
|
+
knownAgents.trackPageviewOrRESTCall(request, response, {
|
|
84
|
+
acpResponseBody: acpResponseBody
|
|
126
85
|
})
|
|
86
|
+
```
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
|
|
88
|
+
```ts
|
|
89
|
+
// For UCP calls ...
|
|
130
90
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}, 30000)
|
|
91
|
+
knownAgents.trackPageviewOrRESTCall(request, response, {
|
|
92
|
+
ucpResponseBody: ucpResponseBody
|
|
93
|
+
})
|
|
135
94
|
```
|
|
136
95
|
|
|
96
|
+
`trackMCPCall` will track MCP implementations of ACP and UCP automatically.
|
|
97
|
+
|
|
137
98
|
### Test Your Integration
|
|
138
99
|
|
|
139
100
|
- Open your project's settings page
|
|
140
101
|
- Click **Send a Test Visit**
|
|
141
102
|
- Click **Realtime**
|
|
142
103
|
|
|
143
|
-
If your website is correctly connected, you should see visits from
|
|
104
|
+
If your website is correctly connected, you should see visits from a test agent in the realtime timeline within a few seconds.
|
|
144
105
|
|
|
145
|
-
##
|
|
106
|
+
## Set Up Automatic Robots.txt ([Automatic Robots.txt](https://knownagents.com/products/automatic-robots-txt))
|
|
146
107
|
|
|
147
|
-
|
|
108
|
+
Serve a robots.txt that continuously updates with rules for new [crawlers, scrapers, AI agents, and other bots](https://knownagents.com/agents) as they're discovered.
|
|
148
109
|
|
|
149
|
-
Use the `generateRobotsTXT`
|
|
110
|
+
Use the `generateRobotsTXT` method. Select which `AgentType`s you want to block, and a string specifying which URLs are disallowed (e.g. `"/"` to disallow all paths).
|
|
150
111
|
|
|
151
112
|
```ts
|
|
152
113
|
import { AgentType } from "@knownagents/sdk"
|
|
@@ -159,29 +120,19 @@ const robotsTxt = await knownAgents.generateRobotsTXT([
|
|
|
159
120
|
], "/")
|
|
160
121
|
```
|
|
161
122
|
|
|
162
|
-
The return value is a plain text robots.txt string. Generate a `robotsTxt` periodically (e.g. once per day
|
|
163
|
-
|
|
164
|
-
## How To Use Agent Identification ([Full Docs](https://knownagents.com/docs/identification))
|
|
123
|
+
The return value is a plain text robots.txt string. Generate a `robotsTxt` periodically (e.g. once per day, using a cron job). Then, serve it from your website's `/robots.txt` endpoint.
|
|
165
124
|
|
|
166
|
-
Use the
|
|
125
|
+
## Use the Agent Identification API ([Agent Identification API](https://knownagents.com/products/agent-identification-api))
|
|
167
126
|
|
|
168
|
-
|
|
127
|
+
Identify and verify [AI agents and other bots](https://knownagents.com/agents) in your own products, from incoming network requests. Authentication uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods. This API can be useful for implementing access policies based on verified agent identity or enriching your own datasets.
|
|
169
128
|
|
|
170
|
-
Call `identifyAgent`
|
|
129
|
+
Call `identifyAgent` to identify a single incoming request:
|
|
171
130
|
|
|
172
131
|
```ts
|
|
173
132
|
const identification = await knownAgents.identifyAgent(request)
|
|
174
|
-
|
|
175
|
-
if (identification.result === "verified") {
|
|
176
|
-
handleVerifiedAgent(identification)
|
|
177
|
-
} else if (identification.result === "verification_failed") {
|
|
178
|
-
handleFailedVerification(identification)
|
|
179
|
-
}
|
|
180
133
|
```
|
|
181
134
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Use `identifyAgents` to identify multiple requests at once:
|
|
135
|
+
Call `identifyAgents` to identify multiple requests at once:
|
|
185
136
|
|
|
186
137
|
```ts
|
|
187
138
|
const identifications = await knownAgents.identifyAgents([
|
|
@@ -196,19 +147,19 @@ const identifications = await knownAgents.identifyAgents([
|
|
|
196
147
|
])
|
|
197
148
|
```
|
|
198
149
|
|
|
199
|
-
|
|
150
|
+
These methods return an object (or array of objects) with the following fields:
|
|
200
151
|
|
|
201
152
|
- `id`: The identifier from the request (if provided)
|
|
202
153
|
- `result`: The identification result:
|
|
203
|
-
- `"verified"`: The agent
|
|
204
|
-
- `"verification_failed"`: The agent was identified but
|
|
205
|
-
- `"
|
|
206
|
-
- `"
|
|
154
|
+
- `"verified"`: The agent was identified and verified
|
|
155
|
+
- `"verification_failed"`: The agent was identified but failed verification (it was spoofed)
|
|
156
|
+
- `"not_verifiable"`: The agent was identified but could not be verified (no method available)
|
|
157
|
+
- `"unknown_agent"`: Not an agent, or the agent is not in the database
|
|
207
158
|
- `agent_id`: The unique ID of the agent (if identified)
|
|
208
|
-
- `agent_token`: The name of the agent (e.g. `"
|
|
159
|
+
- `agent_token`: The name of the agent (e.g. `"Claude-User"`) (if identified)
|
|
209
160
|
- `agent_url`: The documentation URL of the agent (if identified)
|
|
210
|
-
- `agent_type_name`: The type of agent (e.g. `"AI
|
|
211
|
-
- `operator_name`: The company
|
|
161
|
+
- `agent_type_name`: The type of agent (e.g. `"AI Assistant"`) (if identified)
|
|
162
|
+
- `operator_name`: The company operating the agent (e.g. `"Anthropic"`) (if identified)
|
|
212
163
|
|
|
213
164
|
## Requirements
|
|
214
165
|
|
package/dist/KnownAgents.d.ts
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
2
|
import type { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
3
|
-
import type { AgentType, IdentificationRequest, IdentificationResult, TrackPageviewOrRESTCallOptions, VisitRequest } from "./types";
|
|
3
|
+
import type { AgentType, IdentificationRequest, IdentificationResult, KnownAgentsOptions, TrackPageviewOrRESTCallOptions, VisitRequest } from "./types";
|
|
4
4
|
/**
|
|
5
5
|
* Server-side client for Known Agents analytics, robots.txt generation, and agent identification.
|
|
6
6
|
*/
|
|
7
7
|
export declare class KnownAgents {
|
|
8
8
|
private readonly accessToken;
|
|
9
|
+
private readonly flushIntervalInMilliseconds;
|
|
10
|
+
private readonly flushQueueSize;
|
|
11
|
+
private readonly visitQueue;
|
|
12
|
+
private flushTimeout;
|
|
13
|
+
private pendingFlushPromise;
|
|
9
14
|
/**
|
|
10
15
|
* Creates a new instance of the Known Agents client.
|
|
11
16
|
*
|
|
12
17
|
* @param accessToken - Your project's access token.
|
|
18
|
+
* @param options - Optional batching configuration.
|
|
13
19
|
*/
|
|
14
|
-
constructor(accessToken: string);
|
|
20
|
+
constructor(accessToken: string, options?: KnownAgentsOptions);
|
|
15
21
|
/**
|
|
16
22
|
* Tracks a single non-MCP pageview or REST API call for analytics.
|
|
17
|
-
* The visit is
|
|
18
|
-
* This method is non-blocking and handles errors internally.
|
|
23
|
+
* The visit is added to the visit event queue after the response finishes so status, headers, and duration are included.
|
|
24
|
+
* This method is non-blocking and handles upload errors internally.
|
|
19
25
|
*
|
|
20
26
|
* @param request - The incoming Node.js HTTP request.
|
|
21
27
|
* @param response - The outgoing Node.js HTTP response.
|
|
@@ -26,7 +32,7 @@ export declare class KnownAgents {
|
|
|
26
32
|
* Tracks a single MCP call handled by `StreamableHTTPServerTransport`.
|
|
27
33
|
* Call this after connecting the transport and before the transport processes the request so request and response metadata can be captured.
|
|
28
34
|
* Do not use this with transport instances shared across concurrent requests; use `trackVisits()` instead.
|
|
29
|
-
* This method is non-blocking and handles errors internally.
|
|
35
|
+
* This method is non-blocking and handles upload errors internally.
|
|
30
36
|
*
|
|
31
37
|
* @param request - The incoming Node.js HTTP request.
|
|
32
38
|
* @param response - The outgoing Node.js HTTP response.
|
|
@@ -34,13 +40,20 @@ export declare class KnownAgents {
|
|
|
34
40
|
*/
|
|
35
41
|
trackMCPCall(request: IncomingMessage, response: ServerResponse, transport: StreamableHTTPServerTransport): void;
|
|
36
42
|
/**
|
|
37
|
-
*
|
|
38
|
-
* This method is non-blocking and handles errors internally.
|
|
39
|
-
*
|
|
43
|
+
* Adds multiple visits to the visit event queue for analytics.
|
|
44
|
+
* This method is non-blocking and handles upload errors internally.
|
|
45
|
+
* All visit events are uploaded together when the queue size reaches the configured threshold or the flush interval elapses.
|
|
40
46
|
*
|
|
41
47
|
* @param requests - An array of visits.
|
|
42
48
|
*/
|
|
43
49
|
trackVisits(requests: VisitRequest[]): void;
|
|
50
|
+
/**
|
|
51
|
+
* Uploads all events currently in the visit event queue and waits for pending uploads to finish.
|
|
52
|
+
* This method handles upload errors internally.
|
|
53
|
+
*
|
|
54
|
+
* @returns A promise that resolves after all pending upload attempts finish.
|
|
55
|
+
*/
|
|
56
|
+
flush(): Promise<void>;
|
|
44
57
|
/**
|
|
45
58
|
* Generates a robots.txt file that disallows the specified agent types.
|
|
46
59
|
* The generated file stays up-to-date with all current and future agents in the specified categories.
|
|
@@ -70,5 +83,7 @@ export declare class KnownAgents {
|
|
|
70
83
|
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
71
84
|
*/
|
|
72
85
|
identifyAgents(requests: IdentificationRequest[]): Promise<IdentificationResult[]>;
|
|
86
|
+
private scheduleFlush;
|
|
87
|
+
private sendVisits;
|
|
73
88
|
}
|
|
74
89
|
//# sourceMappingURL=KnownAgents.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KnownAgents.d.ts","sourceRoot":"","sources":["../src/KnownAgents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AAC3D,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;
|
|
1
|
+
{"version":3,"file":"KnownAgents.d.ts","sourceRoot":"","sources":["../src/KnownAgents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AAC3D,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;AAGvG,OAAO,KAAK,EACR,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,8BAA8B,EAC9B,YAAY,EACf,MAAM,SAAS,CAAA;AAKhB;;GAEG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAQ;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,mBAAmB,CAAmC;IAE9D;;;;;OAKG;gBACS,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB;IAMjE;;;;;;;;OAQG;IACH,uBAAuB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,8BAA8B,GAAG,IAAI;IAoC3H;;;;;;;;;OASG;IACH,YAAY,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,6BAA6B,GAAG,IAAI;IAoEhH;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IA2B3C;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBtB;;;;;;;;;OASG;IACG,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBzF;;;;;;;OAOG;IACG,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmB5E;;;;;;;OAOG;IACG,cAAc,CAAC,QAAQ,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAsBxF,OAAO,CAAC,aAAa;YAaP,UAAU;CAmB3B"}
|
package/dist/KnownAgents.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KnownAgents = void 0;
|
|
4
|
-
const commerce_1 = require("./commerce");
|
|
5
4
|
const headers_1 = require("./headers");
|
|
6
5
|
const paths_1 = require("./paths");
|
|
6
|
+
const DEFAULT_FLUSH_INTERVAL_IN_MILLISECONDS = 10000;
|
|
7
|
+
const DEFAULT_FLUSH_QUEUE_SIZE = 1;
|
|
7
8
|
/**
|
|
8
9
|
* Server-side client for Known Agents analytics, robots.txt generation, and agent identification.
|
|
9
10
|
*/
|
|
@@ -12,14 +13,19 @@ class KnownAgents {
|
|
|
12
13
|
* Creates a new instance of the Known Agents client.
|
|
13
14
|
*
|
|
14
15
|
* @param accessToken - Your project's access token.
|
|
16
|
+
* @param options - Optional batching configuration.
|
|
15
17
|
*/
|
|
16
|
-
constructor(accessToken) {
|
|
18
|
+
constructor(accessToken, options = {}) {
|
|
19
|
+
this.visitQueue = [];
|
|
20
|
+
this.pendingFlushPromise = Promise.resolve();
|
|
17
21
|
this.accessToken = accessToken;
|
|
22
|
+
this.flushIntervalInMilliseconds = options.flushIntervalInMilliseconds ?? DEFAULT_FLUSH_INTERVAL_IN_MILLISECONDS;
|
|
23
|
+
this.flushQueueSize = options.flushQueueSize ?? DEFAULT_FLUSH_QUEUE_SIZE;
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
26
|
* Tracks a single non-MCP pageview or REST API call for analytics.
|
|
21
|
-
* The visit is
|
|
22
|
-
* This method is non-blocking and handles errors internally.
|
|
27
|
+
* The visit is added to the visit event queue after the response finishes so status, headers, and duration are included.
|
|
28
|
+
* This method is non-blocking and handles upload errors internally.
|
|
23
29
|
*
|
|
24
30
|
* @param request - The incoming Node.js HTTP request.
|
|
25
31
|
* @param response - The outgoing Node.js HTTP response.
|
|
@@ -42,10 +48,8 @@ class KnownAgents {
|
|
|
42
48
|
response_status_code: response?.statusCode,
|
|
43
49
|
response_headers: response.getHeaders(),
|
|
44
50
|
response_duration_in_milliseconds: responseDurationInMilliseconds,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
ucp_response_currency: options?.restUCPResponseBody ? (0, commerce_1.getRESTUCPCurrencyIfPossible)(options?.restUCPResponseBody) : undefined,
|
|
48
|
-
ucp_response_total_amount: options?.restUCPResponseBody ? (0, commerce_1.getRESTUCPTotalAmountIfPossible)(options?.restUCPResponseBody) : undefined,
|
|
51
|
+
acp_response_body: options?.acpResponseBody,
|
|
52
|
+
ucp_response_body: options?.ucpResponseBody,
|
|
49
53
|
created: created.toISOString()
|
|
50
54
|
};
|
|
51
55
|
this.trackVisits([visitRequest]);
|
|
@@ -61,17 +65,16 @@ class KnownAgents {
|
|
|
61
65
|
* Tracks a single MCP call handled by `StreamableHTTPServerTransport`.
|
|
62
66
|
* Call this after connecting the transport and before the transport processes the request so request and response metadata can be captured.
|
|
63
67
|
* Do not use this with transport instances shared across concurrent requests; use `trackVisits()` instead.
|
|
64
|
-
* This method is non-blocking and handles errors internally.
|
|
68
|
+
* This method is non-blocking and handles upload errors internally.
|
|
65
69
|
*
|
|
66
70
|
* @param request - The incoming Node.js HTTP request.
|
|
67
71
|
* @param response - The outgoing Node.js HTTP response.
|
|
68
72
|
* @param transport - The MCP streamable HTTP transport handling the request.
|
|
69
73
|
*/
|
|
70
74
|
trackMCPCall(request, response, transport) {
|
|
71
|
-
const isMCPCall = (0, headers_1.getIsMCPCall)(request.headers);
|
|
72
75
|
const path = request.url;
|
|
73
76
|
const method = request.method;
|
|
74
|
-
if (!
|
|
77
|
+
if (!path || (0, paths_1.getIsPathExcluded)(path) || !method) {
|
|
75
78
|
return;
|
|
76
79
|
}
|
|
77
80
|
const created = new Date();
|
|
@@ -111,10 +114,8 @@ class KnownAgents {
|
|
|
111
114
|
mcp_response_result_is_error: mcpResponseBody.result?.isError,
|
|
112
115
|
mcp_response_error_code: mcpResponseBody.error?.code,
|
|
113
116
|
mcp_response_error_message: mcpResponseBody.error?.message,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
ucp_response_currency: (0, commerce_1.getMCPUCPCurrencyIfPossible)(request, mcpResponseBody),
|
|
117
|
-
ucp_response_total_amount: (0, commerce_1.getMCPUCPTotalAmountIfPossible)(request, mcpResponseBody),
|
|
117
|
+
acp_response_body: (0, headers_1.getIsACPCall)(request.headers) ? mcpResponseBody.result?.structuredContent ?? mcpResponseBody.result : undefined,
|
|
118
|
+
ucp_response_body: (0, headers_1.getIsUCPCall)(request.headers) ? mcpResponseBody.result?.structuredContent ?? mcpResponseBody.result : undefined,
|
|
118
119
|
created: created.toISOString()
|
|
119
120
|
};
|
|
120
121
|
this.trackVisits([visitRequest]);
|
|
@@ -127,9 +128,9 @@ class KnownAgents {
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
130
|
-
*
|
|
131
|
-
* This method is non-blocking and handles errors internally.
|
|
132
|
-
*
|
|
131
|
+
* Adds multiple visits to the visit event queue for analytics.
|
|
132
|
+
* This method is non-blocking and handles upload errors internally.
|
|
133
|
+
* All visit events are uploaded together when the queue size reaches the configured threshold or the flush interval elapses.
|
|
133
134
|
*
|
|
134
135
|
* @param requests - An array of visits.
|
|
135
136
|
*/
|
|
@@ -148,16 +149,32 @@ class KnownAgents {
|
|
|
148
149
|
if (filteredRequests.length === 0) {
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
this.visitQueue.push(...filteredRequests);
|
|
153
|
+
if (this.visitQueue.length >= this.flushQueueSize) {
|
|
154
|
+
void this.flush();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.scheduleFlush();
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Uploads all events currently in the visit event queue and waits for pending uploads to finish.
|
|
161
|
+
* This method handles upload errors internally.
|
|
162
|
+
*
|
|
163
|
+
* @returns A promise that resolves after all pending upload attempts finish.
|
|
164
|
+
*/
|
|
165
|
+
flush() {
|
|
166
|
+
if (this.flushTimeout) {
|
|
167
|
+
clearTimeout(this.flushTimeout);
|
|
168
|
+
this.flushTimeout = undefined;
|
|
169
|
+
}
|
|
170
|
+
const requests = this.visitQueue.splice(0);
|
|
171
|
+
if (requests.length === 0) {
|
|
172
|
+
return this.pendingFlushPromise;
|
|
173
|
+
}
|
|
174
|
+
this.pendingFlushPromise = this.pendingFlushPromise.then(() => {
|
|
175
|
+
return this.sendVisits(requests);
|
|
160
176
|
});
|
|
177
|
+
return this.pendingFlushPromise;
|
|
161
178
|
}
|
|
162
179
|
/**
|
|
163
180
|
* Generates a robots.txt file that disallows the specified agent types.
|
|
@@ -243,5 +260,34 @@ class KnownAgents {
|
|
|
243
260
|
throw new Error(`Known Agents failed to identify agents: ${response.status} ${response.statusText}`);
|
|
244
261
|
}
|
|
245
262
|
}
|
|
263
|
+
scheduleFlush() {
|
|
264
|
+
if (this.flushTimeout) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.flushTimeout = setTimeout(() => {
|
|
268
|
+
this.flushTimeout = undefined;
|
|
269
|
+
void this.flush();
|
|
270
|
+
}, this.flushIntervalInMilliseconds);
|
|
271
|
+
this.flushTimeout.unref();
|
|
272
|
+
}
|
|
273
|
+
async sendVisits(requests) {
|
|
274
|
+
try {
|
|
275
|
+
const response = await fetch("https://api.knownagents.com/visits", {
|
|
276
|
+
method: "POST",
|
|
277
|
+
headers: {
|
|
278
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
279
|
+
"Content-Type": "application/json"
|
|
280
|
+
},
|
|
281
|
+
body: JSON.stringify(requests)
|
|
282
|
+
});
|
|
283
|
+
if (!response.ok) {
|
|
284
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
289
|
+
console.error(`Known Agents failed to track visits: ${message}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
246
292
|
}
|
|
247
293
|
exports.KnownAgents = KnownAgents;
|
package/dist/headers.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import type { IncomingHttpHeaders, OutgoingHttpHeaders } from "http";
|
|
|
2
2
|
export declare function getFilteredHeaders<Headers extends IncomingHttpHeaders | OutgoingHttpHeaders>(headers: Headers): Headers;
|
|
3
3
|
export declare function getIsMCPCall(headers: IncomingHttpHeaders): boolean;
|
|
4
4
|
export declare function getIsUCPCall(headers: IncomingHttpHeaders): boolean;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function getIsACPCall(headers: IncomingHttpHeaders): boolean;
|
|
6
6
|
//# sourceMappingURL=headers.d.ts.map
|
package/dist/headers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAA;AA2BpE,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAUvH;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAA;AA2BpE,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAUvH;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE"}
|
package/dist/headers.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getFilteredHeaders = getFilteredHeaders;
|
|
4
4
|
exports.getIsMCPCall = getIsMCPCall;
|
|
5
5
|
exports.getIsUCPCall = getIsUCPCall;
|
|
6
|
-
exports.
|
|
6
|
+
exports.getIsACPCall = getIsACPCall;
|
|
7
7
|
const filteredHeaderNames = [
|
|
8
8
|
"authorization",
|
|
9
9
|
"proxy-authorization",
|
|
@@ -47,8 +47,8 @@ function getIsUCPCall(headers) {
|
|
|
47
47
|
return key.toLowerCase() === "ucp-agent";
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
function
|
|
51
|
-
return Object.keys(headers).some((key) => {
|
|
50
|
+
function getIsACPCall(headers) {
|
|
51
|
+
return !getIsUCPCall(headers) && Object.keys(headers).some((key) => {
|
|
52
52
|
return key.toLowerCase() === "api-version";
|
|
53
53
|
});
|
|
54
54
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { KnownAgents } from "./KnownAgents";
|
|
2
|
-
export { AgentType, type IdentificationRequest, type IdentificationResult, type
|
|
2
|
+
export { AgentType, type IdentificationRequest, type IdentificationResult, type KnownAgentsOptions, type TrackPageviewOrRESTCallOptions, type VisitRequest } from "./types";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EACH,SAAS,EACT,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EACH,SAAS,EACT,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,8BAA8B,EACnC,KAAK,YAAY,EACpB,MAAM,SAAS,CAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
1
|
import type { IncomingHttpHeaders, OutgoingHttpHeaders } from "http";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Batching configuration for the Known Agents client.
|
|
4
4
|
*/
|
|
5
|
-
export interface
|
|
6
|
-
/** The
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
type?: string;
|
|
11
|
-
amount?: number;
|
|
12
|
-
}[];
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* UCP response details from a REST API call.
|
|
16
|
-
*/
|
|
17
|
-
export interface RESTUCPResponseBody {
|
|
18
|
-
/** The response currency, usually an ISO 4217 code. */
|
|
19
|
-
currency?: string;
|
|
20
|
-
/** UCP totals returned by the REST endpoint. The "total" item is sent to Known Agents. */
|
|
21
|
-
totals?: {
|
|
22
|
-
type?: string;
|
|
23
|
-
amount?: number;
|
|
24
|
-
}[];
|
|
5
|
+
export interface KnownAgentsOptions {
|
|
6
|
+
/** The maximum time a partial visit event queue waits before upload. Defaults to 10,000 milliseconds. */
|
|
7
|
+
flushIntervalInMilliseconds?: number;
|
|
8
|
+
/** The number of visit events in the queue that triggers an upload. Defaults to 1. */
|
|
9
|
+
flushQueueSize?: number;
|
|
25
10
|
}
|
|
26
11
|
/**
|
|
27
12
|
* Additional metadata to include when tracking a pageview or REST API call.
|
|
28
13
|
*/
|
|
29
14
|
export interface TrackPageviewOrRESTCallOptions {
|
|
30
|
-
/** ACP response body
|
|
31
|
-
|
|
32
|
-
/** UCP response body
|
|
33
|
-
|
|
15
|
+
/** The ACP response body. */
|
|
16
|
+
acpResponseBody?: Record<string, unknown>;
|
|
17
|
+
/** The UCP response body. */
|
|
18
|
+
ucpResponseBody?: Record<string, unknown>;
|
|
34
19
|
}
|
|
35
20
|
/**
|
|
36
21
|
* A normalized visit payload for the Known Agents API.
|
|
37
|
-
* Use this with `trackVisits()`
|
|
22
|
+
* Use this with `trackVisits()` to manually add visit events to the queue.
|
|
38
23
|
*/
|
|
39
24
|
export interface VisitRequest {
|
|
40
25
|
/** The URL path and query string of the request (e.g. "/about?foo=bar") */
|
|
@@ -63,14 +48,10 @@ export interface VisitRequest {
|
|
|
63
48
|
mcp_response_error_code?: number;
|
|
64
49
|
/** The MCP response error message */
|
|
65
50
|
mcp_response_error_message?: string;
|
|
66
|
-
/** The ACP response
|
|
67
|
-
|
|
68
|
-
/** The
|
|
69
|
-
|
|
70
|
-
/** The UCP response currency */
|
|
71
|
-
ucp_response_currency?: string;
|
|
72
|
-
/** The UCP response "total" amount */
|
|
73
|
-
ucp_response_total_amount?: number;
|
|
51
|
+
/** The ACP response body */
|
|
52
|
+
acp_response_body?: Record<string, unknown>;
|
|
53
|
+
/** The UCP response body */
|
|
54
|
+
ucp_response_body?: Record<string, unknown>;
|
|
74
55
|
/** The timestamp when the visit occurred (ISO 8601 format) */
|
|
75
56
|
created?: string;
|
|
76
57
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAA;AAEpE;;GAEG;AACH,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAA;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,yGAAyG;IACzG,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,sFAAsF;IACtF,cAAc,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACzC,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,CAAA;IACpB,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAA;IACtB,8EAA8E;IAC9E,eAAe,EAAE,mBAAmB,CAAA;IACpC,oDAAoD;IACpD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,mBAAmB,CAAA;IACtC,4CAA4C;IAC5C,iCAAiC,CAAC,EAAE,MAAM,CAAA;IAC1C,sCAAsC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,kCAAkC;IAClC,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,qCAAqC;IACrC,+BAA+B,CAAC,EAAE,MAAM,CAAA;IACxC,kDAAkD;IAClD,4BAA4B,CAAC,EAAE,OAAO,CAAA;IACtC,kCAAkC;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,qCAAqC;IACrC,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,4BAA4B;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,4BAA4B;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAClC,4FAA4F;IAC5F,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,eAAe,EAAE,mBAAmB,CAAA;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,oDAAoD;IACpD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;;;;;OAMG;IACH,MAAM,EAAE,UAAU,GAAG,qBAAqB,GAAG,eAAe,GAAG,gBAAgB,CAAA;IAC/E,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;GAEG;AACH,oBAAY,SAAS;IACjB,OAAO,aAAa;IACpB,WAAW,iBAAiB;IAC5B,aAAa,oBAAoB;IACjC,cAAc,qBAAqB;IACnC,aAAa,oBAAoB;IACjC,eAAe,sBAAsB;IACrC,QAAQ,aAAa;IACrB,cAAc,oBAAoB;IAClC,eAAe,qBAAqB;IACpC,OAAO,YAAY;IACnB,oBAAoB,0BAA0B;IAC9C,OAAO,YAAY;IACnB,UAAU,gBAAgB;IAC1B,mBAAmB,0BAA0B;IAC7C,eAAe,qBAAqB;IACpC,mBAAmB,0BAA0B;CAChD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knownagents/sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "The official Node.js SDK for Known Agents: AI agent analytics (bot traffic), LLM referral tracking, automatic robots.txt, agent
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "The official Node.js SDK for Known Agents: AI agent analytics (bot traffic), LLM referral tracking, automatic robots.txt, agent identification",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"web scraping",
|
|
33
33
|
"ai blocking",
|
|
34
34
|
"bot blocking",
|
|
35
|
-
"agent
|
|
35
|
+
"agent identification",
|
|
36
36
|
"bot verification",
|
|
37
37
|
"verify ai agents",
|
|
38
38
|
"web bot auth",
|
package/src/KnownAgents.ts
CHANGED
|
@@ -1,79 +1,46 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "http"
|
|
2
2
|
import type { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
|
|
3
|
-
import {
|
|
4
|
-
getMCPACPCurrencyIfPossible,
|
|
5
|
-
getMCPACPTotalAmountIfPossible,
|
|
6
|
-
getMCPUCPCurrencyIfPossible,
|
|
7
|
-
getMCPUCPTotalAmountIfPossible,
|
|
8
|
-
getRESTACPCurrencyIfPossible,
|
|
9
|
-
getRESTACPTotalAmountIfPossible,
|
|
10
|
-
getRESTUCPCurrencyIfPossible,
|
|
11
|
-
getRESTUCPTotalAmountIfPossible
|
|
12
|
-
} from "./commerce"
|
|
13
|
-
import { getFilteredHeaders, getIsMCPCall } from "./headers"
|
|
3
|
+
import { getFilteredHeaders, getIsACPCall, getIsMCPCall, getIsUCPCall } from "./headers"
|
|
14
4
|
import { getIsPathExcluded } from "./paths"
|
|
15
5
|
import type {
|
|
16
6
|
AgentType,
|
|
17
7
|
IdentificationRequest,
|
|
18
8
|
IdentificationResult,
|
|
9
|
+
KnownAgentsOptions,
|
|
19
10
|
TrackPageviewOrRESTCallOptions,
|
|
20
11
|
VisitRequest
|
|
21
12
|
} from "./types"
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
method: string
|
|
26
|
-
params?: {
|
|
27
|
-
name?: string
|
|
28
|
-
clientInfo?: {
|
|
29
|
-
name?: string
|
|
30
|
-
version?: string
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface MCPResponseBody {
|
|
36
|
-
jsonrpc: string
|
|
37
|
-
result?: {
|
|
38
|
-
isError?: boolean
|
|
39
|
-
currency?: string
|
|
40
|
-
totals?: {
|
|
41
|
-
type?: string
|
|
42
|
-
amount?: number
|
|
43
|
-
}[]
|
|
44
|
-
structuredContent?: {
|
|
45
|
-
currency?: string
|
|
46
|
-
totals?: {
|
|
47
|
-
type?: string
|
|
48
|
-
amount?: number
|
|
49
|
-
}[]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
error?: {
|
|
53
|
-
code?: number
|
|
54
|
-
message?: string
|
|
55
|
-
}
|
|
56
|
-
}
|
|
14
|
+
const DEFAULT_FLUSH_INTERVAL_IN_MILLISECONDS = 10000
|
|
15
|
+
const DEFAULT_FLUSH_QUEUE_SIZE = 1
|
|
57
16
|
|
|
58
17
|
/**
|
|
59
18
|
* Server-side client for Known Agents analytics, robots.txt generation, and agent identification.
|
|
60
19
|
*/
|
|
61
20
|
export class KnownAgents {
|
|
62
21
|
private readonly accessToken: string
|
|
22
|
+
private readonly flushIntervalInMilliseconds: number
|
|
23
|
+
private readonly flushQueueSize: number
|
|
24
|
+
private readonly visitQueue: VisitRequest[] = []
|
|
25
|
+
private flushTimeout: NodeJS.Timeout | undefined
|
|
26
|
+
private pendingFlushPromise: Promise<void> = Promise.resolve()
|
|
63
27
|
|
|
64
28
|
/**
|
|
65
29
|
* Creates a new instance of the Known Agents client.
|
|
66
|
-
*
|
|
30
|
+
*
|
|
67
31
|
* @param accessToken - Your project's access token.
|
|
32
|
+
* @param options - Optional batching configuration.
|
|
68
33
|
*/
|
|
69
|
-
constructor(accessToken: string) {
|
|
34
|
+
constructor(accessToken: string, options: KnownAgentsOptions = {}) {
|
|
70
35
|
this.accessToken = accessToken
|
|
36
|
+
this.flushIntervalInMilliseconds = options.flushIntervalInMilliseconds ?? DEFAULT_FLUSH_INTERVAL_IN_MILLISECONDS
|
|
37
|
+
this.flushQueueSize = options.flushQueueSize ?? DEFAULT_FLUSH_QUEUE_SIZE
|
|
71
38
|
}
|
|
72
39
|
|
|
73
40
|
/**
|
|
74
41
|
* Tracks a single non-MCP pageview or REST API call for analytics.
|
|
75
|
-
* The visit is
|
|
76
|
-
* This method is non-blocking and handles errors internally.
|
|
42
|
+
* The visit is added to the visit event queue after the response finishes so status, headers, and duration are included.
|
|
43
|
+
* This method is non-blocking and handles upload errors internally.
|
|
77
44
|
*
|
|
78
45
|
* @param request - The incoming Node.js HTTP request.
|
|
79
46
|
* @param response - The outgoing Node.js HTTP response.
|
|
@@ -100,10 +67,8 @@ export class KnownAgents {
|
|
|
100
67
|
response_status_code: response?.statusCode,
|
|
101
68
|
response_headers: response.getHeaders(),
|
|
102
69
|
response_duration_in_milliseconds: responseDurationInMilliseconds,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
ucp_response_currency: options?.restUCPResponseBody ? getRESTUCPCurrencyIfPossible(options?.restUCPResponseBody) : undefined,
|
|
106
|
-
ucp_response_total_amount: options?.restUCPResponseBody ? getRESTUCPTotalAmountIfPossible(options?.restUCPResponseBody) : undefined,
|
|
70
|
+
acp_response_body: options?.acpResponseBody,
|
|
71
|
+
ucp_response_body: options?.ucpResponseBody,
|
|
107
72
|
created: created.toISOString()
|
|
108
73
|
}
|
|
109
74
|
|
|
@@ -121,35 +86,34 @@ export class KnownAgents {
|
|
|
121
86
|
* Tracks a single MCP call handled by `StreamableHTTPServerTransport`.
|
|
122
87
|
* Call this after connecting the transport and before the transport processes the request so request and response metadata can be captured.
|
|
123
88
|
* Do not use this with transport instances shared across concurrent requests; use `trackVisits()` instead.
|
|
124
|
-
* This method is non-blocking and handles errors internally.
|
|
89
|
+
* This method is non-blocking and handles upload errors internally.
|
|
125
90
|
*
|
|
126
91
|
* @param request - The incoming Node.js HTTP request.
|
|
127
92
|
* @param response - The outgoing Node.js HTTP response.
|
|
128
93
|
* @param transport - The MCP streamable HTTP transport handling the request.
|
|
129
94
|
*/
|
|
130
95
|
trackMCPCall(request: IncomingMessage, response: ServerResponse, transport: StreamableHTTPServerTransport): void {
|
|
131
|
-
const isMCPCall = getIsMCPCall(request.headers)
|
|
132
96
|
const path = request.url
|
|
133
97
|
const method = request.method
|
|
134
98
|
|
|
135
|
-
if (!
|
|
99
|
+
if (!path || getIsPathExcluded(path) || !method) {
|
|
136
100
|
return
|
|
137
101
|
}
|
|
138
102
|
|
|
139
103
|
const created = new Date()
|
|
140
104
|
|
|
141
|
-
let mcpRequestBody:
|
|
142
|
-
let mcpResponseBody:
|
|
105
|
+
let mcpRequestBody: any
|
|
106
|
+
let mcpResponseBody: any
|
|
143
107
|
|
|
144
108
|
const originalTransportOnMessage = transport.onmessage
|
|
145
109
|
transport.onmessage = (message, extra): void => {
|
|
146
|
-
mcpRequestBody = message
|
|
110
|
+
mcpRequestBody = message
|
|
147
111
|
originalTransportOnMessage?.(message, extra)
|
|
148
112
|
}
|
|
149
|
-
|
|
113
|
+
|
|
150
114
|
const originalTransportSend = transport.send
|
|
151
115
|
transport.send = async (message, options): Promise<void> => {
|
|
152
|
-
mcpResponseBody = message
|
|
116
|
+
mcpResponseBody = message
|
|
153
117
|
await originalTransportSend.call(transport, message, options)
|
|
154
118
|
}
|
|
155
119
|
|
|
@@ -181,10 +145,8 @@ export class KnownAgents {
|
|
|
181
145
|
mcp_response_result_is_error: mcpResponseBody.result?.isError,
|
|
182
146
|
mcp_response_error_code: mcpResponseBody.error?.code,
|
|
183
147
|
mcp_response_error_message: mcpResponseBody.error?.message,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
ucp_response_currency: getMCPUCPCurrencyIfPossible(request, mcpResponseBody),
|
|
187
|
-
ucp_response_total_amount: getMCPUCPTotalAmountIfPossible(request, mcpResponseBody),
|
|
148
|
+
acp_response_body: getIsACPCall(request.headers) ? mcpResponseBody.result?.structuredContent ?? mcpResponseBody.result : undefined,
|
|
149
|
+
ucp_response_body: getIsUCPCall(request.headers) ? mcpResponseBody.result?.structuredContent ?? mcpResponseBody.result : undefined,
|
|
188
150
|
created: created.toISOString()
|
|
189
151
|
}
|
|
190
152
|
|
|
@@ -197,11 +159,11 @@ export class KnownAgents {
|
|
|
197
159
|
response.once("finish", sendVisit)
|
|
198
160
|
}
|
|
199
161
|
}
|
|
200
|
-
|
|
162
|
+
|
|
201
163
|
/**
|
|
202
|
-
*
|
|
203
|
-
* This method is non-blocking and handles errors internally.
|
|
204
|
-
*
|
|
164
|
+
* Adds multiple visits to the visit event queue for analytics.
|
|
165
|
+
* This method is non-blocking and handles upload errors internally.
|
|
166
|
+
* All visit events are uploaded together when the queue size reaches the configured threshold or the flush interval elapses.
|
|
205
167
|
*
|
|
206
168
|
* @param requests - An array of visits.
|
|
207
169
|
*/
|
|
@@ -222,18 +184,41 @@ export class KnownAgents {
|
|
|
222
184
|
return
|
|
223
185
|
}
|
|
224
186
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
187
|
+
this.visitQueue.push(...filteredRequests)
|
|
188
|
+
|
|
189
|
+
if (this.visitQueue.length >= this.flushQueueSize) {
|
|
190
|
+
void this.flush()
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.scheduleFlush()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Uploads all events currently in the visit event queue and waits for pending uploads to finish.
|
|
199
|
+
* This method handles upload errors internally.
|
|
200
|
+
*
|
|
201
|
+
* @returns A promise that resolves after all pending upload attempts finish.
|
|
202
|
+
*/
|
|
203
|
+
flush(): Promise<void> {
|
|
204
|
+
if (this.flushTimeout) {
|
|
205
|
+
clearTimeout(this.flushTimeout)
|
|
206
|
+
this.flushTimeout = undefined
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const requests = this.visitQueue.splice(0)
|
|
210
|
+
|
|
211
|
+
if (requests.length === 0) {
|
|
212
|
+
return this.pendingFlushPromise
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.pendingFlushPromise = this.pendingFlushPromise.then(() => {
|
|
216
|
+
return this.sendVisits(requests)
|
|
234
217
|
})
|
|
218
|
+
|
|
219
|
+
return this.pendingFlushPromise
|
|
235
220
|
}
|
|
236
|
-
|
|
221
|
+
|
|
237
222
|
/**
|
|
238
223
|
* Generates a robots.txt file that disallows the specified agent types.
|
|
239
224
|
* The generated file stays up-to-date with all current and future agents in the specified categories.
|
|
@@ -263,7 +248,7 @@ export class KnownAgents {
|
|
|
263
248
|
throw new Error(`Known Agents failed to generate robots.txt: ${response.status} ${response.statusText}`)
|
|
264
249
|
}
|
|
265
250
|
}
|
|
266
|
-
|
|
251
|
+
|
|
267
252
|
/**
|
|
268
253
|
* Identifies and verifies a single agent from an HTTP request.
|
|
269
254
|
* Uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods.
|
|
@@ -321,4 +306,36 @@ export class KnownAgents {
|
|
|
321
306
|
}
|
|
322
307
|
}
|
|
323
308
|
|
|
309
|
+
private scheduleFlush(): void {
|
|
310
|
+
if (this.flushTimeout) {
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.flushTimeout = setTimeout(() => {
|
|
315
|
+
this.flushTimeout = undefined
|
|
316
|
+
void this.flush()
|
|
317
|
+
}, this.flushIntervalInMilliseconds)
|
|
318
|
+
|
|
319
|
+
this.flushTimeout.unref()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async sendVisits(requests: VisitRequest[]): Promise<void> {
|
|
323
|
+
try {
|
|
324
|
+
const response = await fetch("https://api.knownagents.com/visits", {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers: {
|
|
327
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
328
|
+
"Content-Type": "application/json"
|
|
329
|
+
},
|
|
330
|
+
body: JSON.stringify(requests)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
if (!response.ok) {
|
|
334
|
+
throw new Error(`${response.status} ${response.statusText}`)
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
338
|
+
console.error(`Known Agents failed to track visits: ${message}`)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
324
341
|
}
|
package/src/headers.ts
CHANGED
|
@@ -49,8 +49,8 @@ export function getIsUCPCall(headers: IncomingHttpHeaders): boolean {
|
|
|
49
49
|
})
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export function
|
|
53
|
-
return Object.keys(headers).some((key) => {
|
|
52
|
+
export function getIsACPCall(headers: IncomingHttpHeaders): boolean {
|
|
53
|
+
return !getIsUCPCall(headers) && Object.keys(headers).some((key) => {
|
|
54
54
|
return key.toLowerCase() === "api-version"
|
|
55
55
|
})
|
|
56
56
|
}
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,44 +1,28 @@
|
|
|
1
1
|
import type { IncomingHttpHeaders, OutgoingHttpHeaders } from "http"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Batching configuration for the Known Agents client.
|
|
5
5
|
*/
|
|
6
|
-
export interface
|
|
7
|
-
/** The
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
type?: string
|
|
12
|
-
amount?: number
|
|
13
|
-
}[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* UCP response details from a REST API call.
|
|
18
|
-
*/
|
|
19
|
-
export interface RESTUCPResponseBody {
|
|
20
|
-
/** The response currency, usually an ISO 4217 code. */
|
|
21
|
-
currency?: string
|
|
22
|
-
/** UCP totals returned by the REST endpoint. The "total" item is sent to Known Agents. */
|
|
23
|
-
totals?: {
|
|
24
|
-
type?: string
|
|
25
|
-
amount?: number
|
|
26
|
-
}[]
|
|
6
|
+
export interface KnownAgentsOptions {
|
|
7
|
+
/** The maximum time a partial visit event queue waits before upload. Defaults to 10,000 milliseconds. */
|
|
8
|
+
flushIntervalInMilliseconds?: number
|
|
9
|
+
/** The number of visit events in the queue that triggers an upload. Defaults to 1. */
|
|
10
|
+
flushQueueSize?: number
|
|
27
11
|
}
|
|
28
12
|
|
|
29
13
|
/**
|
|
30
14
|
* Additional metadata to include when tracking a pageview or REST API call.
|
|
31
15
|
*/
|
|
32
16
|
export interface TrackPageviewOrRESTCallOptions {
|
|
33
|
-
/** ACP response body
|
|
34
|
-
|
|
35
|
-
/** UCP response body
|
|
36
|
-
|
|
17
|
+
/** The ACP response body. */
|
|
18
|
+
acpResponseBody?: Record<string, unknown>
|
|
19
|
+
/** The UCP response body. */
|
|
20
|
+
ucpResponseBody?: Record<string, unknown>
|
|
37
21
|
}
|
|
38
22
|
|
|
39
23
|
/**
|
|
40
24
|
* A normalized visit payload for the Known Agents API.
|
|
41
|
-
* Use this with `trackVisits()`
|
|
25
|
+
* Use this with `trackVisits()` to manually add visit events to the queue.
|
|
42
26
|
*/
|
|
43
27
|
export interface VisitRequest {
|
|
44
28
|
/** The URL path and query string of the request (e.g. "/about?foo=bar") */
|
|
@@ -67,14 +51,10 @@ export interface VisitRequest {
|
|
|
67
51
|
mcp_response_error_code?: number
|
|
68
52
|
/** The MCP response error message */
|
|
69
53
|
mcp_response_error_message?: string
|
|
70
|
-
/** The ACP response
|
|
71
|
-
|
|
72
|
-
/** The
|
|
73
|
-
|
|
74
|
-
/** The UCP response currency */
|
|
75
|
-
ucp_response_currency?: string
|
|
76
|
-
/** The UCP response "total" amount */
|
|
77
|
-
ucp_response_total_amount?: number
|
|
54
|
+
/** The ACP response body */
|
|
55
|
+
acp_response_body?: Record<string, unknown>
|
|
56
|
+
/** The UCP response body */
|
|
57
|
+
ucp_response_body?: Record<string, unknown>
|
|
78
58
|
/** The timestamp when the visit occurred (ISO 8601 format) */
|
|
79
59
|
created?: string
|
|
80
60
|
}
|
package/dist/commerce.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage } from "http";
|
|
2
|
-
import type { RESTACPResponseBody, RESTUCPResponseBody } from "./types";
|
|
3
|
-
interface MCPCommerceResponseBody {
|
|
4
|
-
result?: {
|
|
5
|
-
currency?: string;
|
|
6
|
-
totals?: {
|
|
7
|
-
type?: string;
|
|
8
|
-
amount?: number;
|
|
9
|
-
}[];
|
|
10
|
-
structuredContent?: {
|
|
11
|
-
currency?: string;
|
|
12
|
-
totals?: {
|
|
13
|
-
type?: string;
|
|
14
|
-
amount?: number;
|
|
15
|
-
}[];
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
export declare function getRESTACPCurrencyIfPossible(restACPResponseBody: RESTACPResponseBody): string | undefined;
|
|
20
|
-
export declare function getRESTACPTotalAmountIfPossible(restACPResponseBody: RESTACPResponseBody): number | undefined;
|
|
21
|
-
export declare function getMCPACPCurrencyIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): string | undefined;
|
|
22
|
-
export declare function getMCPACPTotalAmountIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): number | undefined;
|
|
23
|
-
export declare function getRESTUCPCurrencyIfPossible(restUCPResponseBody: RESTUCPResponseBody): string | undefined;
|
|
24
|
-
export declare function getRESTUCPTotalAmountIfPossible(restUCPResponseBody: RESTUCPResponseBody): number | undefined;
|
|
25
|
-
export declare function getMCPUCPCurrencyIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): string | undefined;
|
|
26
|
-
export declare function getMCPUCPTotalAmountIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): number | undefined;
|
|
27
|
-
export {};
|
|
28
|
-
//# sourceMappingURL=commerce.d.ts.map
|
package/dist/commerce.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commerce.d.ts","sourceRoot":"","sources":["../src/commerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAEvE,UAAU,uBAAuB;IAC7B,MAAM,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE;YACL,IAAI,CAAC,EAAE,MAAM,CAAA;YACb,MAAM,CAAC,EAAE,MAAM,CAAA;SAClB,EAAE,CAAA;QACH,iBAAiB,CAAC,EAAE;YAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;YACjB,MAAM,CAAC,EAAE;gBACL,IAAI,CAAC,EAAE,MAAM,CAAA;gBACb,MAAM,CAAC,EAAE,MAAM,CAAA;aAClB,EAAE,CAAA;SACN,CAAA;KACJ,CAAA;CACJ;AAED,wBAAgB,4BAA4B,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAEzG;AAED,wBAAgB,+BAA+B,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAI5G;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAGlI;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAOrI;AAED,wBAAgB,4BAA4B,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAEzG;AAED,wBAAgB,+BAA+B,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAI5G;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAGlI;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAOrI"}
|
package/dist/commerce.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRESTACPCurrencyIfPossible = getRESTACPCurrencyIfPossible;
|
|
4
|
-
exports.getRESTACPTotalAmountIfPossible = getRESTACPTotalAmountIfPossible;
|
|
5
|
-
exports.getMCPACPCurrencyIfPossible = getMCPACPCurrencyIfPossible;
|
|
6
|
-
exports.getMCPACPTotalAmountIfPossible = getMCPACPTotalAmountIfPossible;
|
|
7
|
-
exports.getRESTUCPCurrencyIfPossible = getRESTUCPCurrencyIfPossible;
|
|
8
|
-
exports.getRESTUCPTotalAmountIfPossible = getRESTUCPTotalAmountIfPossible;
|
|
9
|
-
exports.getMCPUCPCurrencyIfPossible = getMCPUCPCurrencyIfPossible;
|
|
10
|
-
exports.getMCPUCPTotalAmountIfPossible = getMCPUCPTotalAmountIfPossible;
|
|
11
|
-
const headers_1 = require("./headers");
|
|
12
|
-
function getRESTACPCurrencyIfPossible(restACPResponseBody) {
|
|
13
|
-
return restACPResponseBody?.currency;
|
|
14
|
-
}
|
|
15
|
-
function getRESTACPTotalAmountIfPossible(restACPResponseBody) {
|
|
16
|
-
return restACPResponseBody?.totals?.find((total) => {
|
|
17
|
-
return total.type == "total";
|
|
18
|
-
})?.amount;
|
|
19
|
-
}
|
|
20
|
-
function getMCPACPCurrencyIfPossible(request, mcpResponseBody) {
|
|
21
|
-
const isProbablyACP = !(0, headers_1.getIsUCPCall)(request.headers) && (0, headers_1.getIsProbablyACPCall)(request.headers);
|
|
22
|
-
return isProbablyACP ? mcpResponseBody.result?.currency : undefined;
|
|
23
|
-
}
|
|
24
|
-
function getMCPACPTotalAmountIfPossible(request, mcpResponseBody) {
|
|
25
|
-
const isProbablyACP = !(0, headers_1.getIsUCPCall)(request.headers) && (0, headers_1.getIsProbablyACPCall)(request.headers);
|
|
26
|
-
return isProbablyACP
|
|
27
|
-
? mcpResponseBody.result?.totals?.find((total) => {
|
|
28
|
-
return total.type == "total";
|
|
29
|
-
})?.amount
|
|
30
|
-
: undefined;
|
|
31
|
-
}
|
|
32
|
-
function getRESTUCPCurrencyIfPossible(restUCPResponseBody) {
|
|
33
|
-
return restUCPResponseBody?.currency;
|
|
34
|
-
}
|
|
35
|
-
function getRESTUCPTotalAmountIfPossible(restUCPResponseBody) {
|
|
36
|
-
return restUCPResponseBody?.totals?.find((total) => {
|
|
37
|
-
return total.type == "total";
|
|
38
|
-
})?.amount;
|
|
39
|
-
}
|
|
40
|
-
function getMCPUCPCurrencyIfPossible(request, mcpResponseBody) {
|
|
41
|
-
const isUCP = (0, headers_1.getIsUCPCall)(request.headers);
|
|
42
|
-
return isUCP ? mcpResponseBody.result?.structuredContent?.currency : undefined;
|
|
43
|
-
}
|
|
44
|
-
function getMCPUCPTotalAmountIfPossible(request, mcpResponseBody) {
|
|
45
|
-
const isUCP = (0, headers_1.getIsUCPCall)(request.headers);
|
|
46
|
-
return isUCP
|
|
47
|
-
? mcpResponseBody.result?.structuredContent?.totals?.find((total) => {
|
|
48
|
-
return total.type == "total";
|
|
49
|
-
})?.amount
|
|
50
|
-
: undefined;
|
|
51
|
-
}
|
package/src/commerce.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage } from "http"
|
|
2
|
-
import { getIsProbablyACPCall, getIsUCPCall } from "./headers"
|
|
3
|
-
import type { RESTACPResponseBody, RESTUCPResponseBody } from "./types"
|
|
4
|
-
|
|
5
|
-
interface MCPCommerceResponseBody {
|
|
6
|
-
result?: {
|
|
7
|
-
currency?: string
|
|
8
|
-
totals?: {
|
|
9
|
-
type?: string
|
|
10
|
-
amount?: number
|
|
11
|
-
}[]
|
|
12
|
-
structuredContent?: {
|
|
13
|
-
currency?: string
|
|
14
|
-
totals?: {
|
|
15
|
-
type?: string
|
|
16
|
-
amount?: number
|
|
17
|
-
}[]
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function getRESTACPCurrencyIfPossible(restACPResponseBody: RESTACPResponseBody): string | undefined {
|
|
23
|
-
return restACPResponseBody?.currency
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getRESTACPTotalAmountIfPossible(restACPResponseBody: RESTACPResponseBody): number | undefined {
|
|
27
|
-
return restACPResponseBody?.totals?.find((total) => {
|
|
28
|
-
return total.type == "total"
|
|
29
|
-
})?.amount
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function getMCPACPCurrencyIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): string | undefined {
|
|
33
|
-
const isProbablyACP = !getIsUCPCall(request.headers) && getIsProbablyACPCall(request.headers)
|
|
34
|
-
return isProbablyACP ? mcpResponseBody.result?.currency : undefined
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getMCPACPTotalAmountIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): number | undefined {
|
|
38
|
-
const isProbablyACP = !getIsUCPCall(request.headers) && getIsProbablyACPCall(request.headers)
|
|
39
|
-
return isProbablyACP
|
|
40
|
-
? mcpResponseBody.result?.totals?.find((total) => {
|
|
41
|
-
return total.type == "total"
|
|
42
|
-
})?.amount
|
|
43
|
-
: undefined
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function getRESTUCPCurrencyIfPossible(restUCPResponseBody: RESTUCPResponseBody): string | undefined {
|
|
47
|
-
return restUCPResponseBody?.currency
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getRESTUCPTotalAmountIfPossible(restUCPResponseBody: RESTUCPResponseBody): number | undefined {
|
|
51
|
-
return restUCPResponseBody?.totals?.find((total) => {
|
|
52
|
-
return total.type == "total"
|
|
53
|
-
})?.amount
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function getMCPUCPCurrencyIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): string | undefined {
|
|
57
|
-
const isUCP = getIsUCPCall(request.headers)
|
|
58
|
-
return isUCP ? mcpResponseBody.result?.structuredContent?.currency : undefined
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function getMCPUCPTotalAmountIfPossible(request: IncomingMessage, mcpResponseBody: MCPCommerceResponseBody): number | undefined {
|
|
62
|
-
const isUCP = getIsUCPCall(request.headers)
|
|
63
|
-
return isUCP
|
|
64
|
-
? mcpResponseBody.result?.structuredContent?.totals?.find((total) => {
|
|
65
|
-
return total.type == "total"
|
|
66
|
-
})?.amount
|
|
67
|
-
: undefined
|
|
68
|
-
}
|