@knownagents/sdk 1.11.0 → 2.1.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 +92 -65
- package/dist/KnownAgents.d.ts +74 -0
- package/dist/KnownAgents.d.ts.map +1 -0
- package/dist/KnownAgents.js +242 -0
- package/dist/commerce.d.ts +28 -0
- package/dist/commerce.d.ts.map +1 -0
- package/dist/commerce.js +51 -0
- package/dist/headers.d.ts +6 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +54 -0
- package/dist/index.d.ts +2 -156
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -253
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +72 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/package.json +14 -1
- package/src/KnownAgents.ts +275 -0
- package/src/headers.ts +56 -0
- package/src/index.ts +8 -353
- package/src/paths.ts +72 -0
- package/src/types.ts +109 -0
- package/tsconfig.json +0 -16
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.org/package/@knownagents/sdk)
|
|
4
4
|
|
|
5
|
-
This library provides convenient access to [Known Agents](https://knownagents.com/) from server-side TypeScript or JavaScript.
|
|
5
|
+
This library provides convenient access to [Known Agents](https://knownagents.com/) from server-side Node.js applications written in TypeScript or JavaScript.
|
|
6
6
|
|
|
7
7
|
## Install the Package
|
|
8
8
|
|
|
@@ -24,73 +24,110 @@ const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN")
|
|
|
24
24
|
|
|
25
25
|
## How To Set Up Agent & LLM Analytics ([Full Docs](https://knownagents.com/docs/analytics))
|
|
26
26
|
|
|
27
|
-
Get realtime insight into
|
|
27
|
+
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.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### Track Pageviews and REST Calls
|
|
30
|
+
|
|
31
|
+
Call `trackPageviewOrRESTCall` with the incoming request and outgoing response. If you can, do this in middleware.
|
|
30
32
|
|
|
31
33
|
```ts
|
|
32
|
-
knownAgents.
|
|
34
|
+
knownAgents.trackPageviewOrRESTCall(request, response)
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
MCP calls are skipped automatically by `trackPageviewOrRESTCall`. Use `trackMCPCall` to track MCP calls.
|
|
38
|
+
|
|
39
|
+
High-traffic websites should not use this method. Instead, batch visits and send them periodically with `trackVisits` (see below).
|
|
40
|
+
|
|
41
|
+
#### Agentic Commerce Calls
|
|
42
|
+
|
|
43
|
+
For REST implementations of ACP and UCP, include the response body.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// For ACP calls ...
|
|
47
|
+
|
|
48
|
+
knownAgents.trackPageviewOrRESTCall(request, response, {
|
|
49
|
+
acpResponseBody: acpResponseBody
|
|
50
|
+
})
|
|
51
|
+
```
|
|
36
52
|
|
|
37
53
|
```ts
|
|
38
|
-
|
|
54
|
+
// For UCP calls ...
|
|
55
|
+
|
|
56
|
+
knownAgents.trackPageviewOrRESTCall(request, response, {
|
|
57
|
+
ucpResponseBody: ucpResponseBody
|
|
58
|
+
})
|
|
39
59
|
```
|
|
40
60
|
|
|
41
|
-
###
|
|
61
|
+
### Track MCP Calls
|
|
42
62
|
|
|
43
|
-
|
|
63
|
+
For MCP servers that use `StreamableHTTPServerTransport`, call `trackMCPCall` after connecting the transport and before the transport handles the request.
|
|
44
64
|
|
|
45
|
-
|
|
65
|
+
High-traffic websites, or those that use the same transport instance to handle multiple concurrent requests, should not use this method. Instead, batch visits and send them periodically with `trackVisits` (see below).
|
|
46
66
|
|
|
47
67
|
```ts
|
|
48
|
-
import
|
|
49
|
-
import {
|
|
68
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
|
|
69
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
|
|
50
70
|
|
|
51
|
-
|
|
52
|
-
const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN")
|
|
71
|
+
// ...
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const duration = Date.now() - start
|
|
58
|
-
knownAgents.trackVisit(req, res, duration)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
next()
|
|
73
|
+
const mcpServer = new McpServer({
|
|
74
|
+
name: SERVER_NAME,
|
|
75
|
+
version: SERVER_VERSION,
|
|
62
76
|
})
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
const transport = new StreamableHTTPServerTransport({
|
|
79
|
+
sessionIdGenerator: undefined
|
|
66
80
|
})
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
// ...
|
|
83
|
+
|
|
84
|
+
await mcpServer.connect(transport)
|
|
85
|
+
|
|
86
|
+
knownAgents.trackMCPCall(request, response, transport)
|
|
87
|
+
|
|
88
|
+
await transport.handleRequest(request, response, parsedBody)
|
|
69
89
|
```
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
#### Agentic Commerce Calls
|
|
92
|
+
|
|
93
|
+
This method will track MCP implementations of ACP and UCP automatically.
|
|
72
94
|
|
|
73
|
-
|
|
95
|
+
### Batching for High-Traffic Websites
|
|
96
|
+
|
|
97
|
+
High-traffic websites should batch multiple visits together and send them periodically (e.g. every 30 seconds) using `trackVisits`. This method can track pageviews, REST, MCP, ACP, and UCP calls. Here's an example with Express middleware:
|
|
74
98
|
|
|
75
99
|
```ts
|
|
76
|
-
import
|
|
100
|
+
import express from "express"
|
|
101
|
+
import { KnownAgents, type VisitRequest } from "@knownagents/sdk"
|
|
77
102
|
|
|
103
|
+
const app = express()
|
|
104
|
+
|
|
105
|
+
const knownAgents = new KnownAgents("YOUR_ACCESS_TOKEN")
|
|
78
106
|
const visits: VisitRequest[] = []
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
app.use((request, response, next) => {
|
|
109
|
+
const created = new Date()
|
|
110
|
+
|
|
111
|
+
response.once("finish", () => {
|
|
112
|
+
visits.push({
|
|
113
|
+
request_path: request.url,
|
|
114
|
+
request_method: request.method,
|
|
115
|
+
request_headers: request.headers,
|
|
116
|
+
response_status_code: response.statusCode,
|
|
117
|
+
response_headers: response.getHeaders(),
|
|
118
|
+
response_duration_in_milliseconds: Date.now() - created.getTime(),
|
|
119
|
+
created: created.toISOString()
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
next()
|
|
88
124
|
})
|
|
89
125
|
|
|
90
|
-
// Send batch periodically
|
|
91
126
|
setInterval(() => {
|
|
92
|
-
|
|
93
|
-
|
|
127
|
+
const batch = visits.splice(0)
|
|
128
|
+
|
|
129
|
+
if (batch.length > 0) {
|
|
130
|
+
knownAgents.trackVisits(batch)
|
|
94
131
|
}
|
|
95
132
|
}, 30000)
|
|
96
133
|
```
|
|
@@ -101,48 +138,38 @@ setInterval(() => {
|
|
|
101
138
|
- Click **Send a Test Visit**
|
|
102
139
|
- Click **Realtime**
|
|
103
140
|
|
|
104
|
-
If your website is correctly connected, you should see visits from
|
|
141
|
+
If your website is correctly connected, you should see visits from a test agent in the realtime timeline within a few seconds.
|
|
105
142
|
|
|
106
143
|
## How To Set Up Automatic Robots.txt ([Full Docs](https://knownagents.com/docs/robots-txt))
|
|
107
144
|
|
|
108
|
-
|
|
145
|
+
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.
|
|
109
146
|
|
|
110
|
-
Use the `generateRobotsTXT`
|
|
147
|
+
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).
|
|
111
148
|
|
|
112
149
|
```ts
|
|
150
|
+
import { AgentType } from "@knownagents/sdk"
|
|
151
|
+
|
|
113
152
|
const robotsTxt = await knownAgents.generateRobotsTXT([
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// ...
|
|
153
|
+
AgentType.AIDataScraper,
|
|
154
|
+
AgentType.AIDataProvider,
|
|
155
|
+
AgentType.Scraper,
|
|
156
|
+
AgentType.SEOCrawler
|
|
119
157
|
], "/")
|
|
120
|
-
|
|
121
158
|
```
|
|
122
159
|
|
|
123
|
-
The return value is a plain text robots.txt string. Generate a `robotsTxt` periodically (e.g. once per day
|
|
124
|
-
|
|
125
|
-
## How To Use Agent Identification ([Full Docs](https://knownagents.com/docs/identification))
|
|
160
|
+
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.
|
|
126
161
|
|
|
127
|
-
Use the
|
|
162
|
+
## How To Use the Agent Identification API ([Full Docs](https://knownagents.com/docs/identification))
|
|
128
163
|
|
|
129
|
-
|
|
164
|
+
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.
|
|
130
165
|
|
|
131
|
-
Call `identifyAgent`
|
|
166
|
+
Call `identifyAgent` to identify a single incoming request:
|
|
132
167
|
|
|
133
168
|
```ts
|
|
134
169
|
const identification = await knownAgents.identifyAgent(request)
|
|
135
|
-
|
|
136
|
-
if (identification.result === "verified") {
|
|
137
|
-
// Agent is legitimate
|
|
138
|
-
} else if (identification.result === "verification_failed") {
|
|
139
|
-
// Agent is not legitimate
|
|
140
|
-
}
|
|
141
170
|
```
|
|
142
171
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Use `identifyAgents` to identify multiple requests at once:
|
|
172
|
+
Call `identifyAgents` to identify multiple requests at once:
|
|
146
173
|
|
|
147
174
|
```ts
|
|
148
175
|
const identifications = await knownAgents.identifyAgents([
|
|
@@ -157,7 +184,7 @@ const identifications = await knownAgents.identifyAgents([
|
|
|
157
184
|
])
|
|
158
185
|
```
|
|
159
186
|
|
|
160
|
-
|
|
187
|
+
These methods return an object (or array of objects) with the following fields:
|
|
161
188
|
|
|
162
189
|
- `id`: The identifier from the request (if provided)
|
|
163
190
|
- `result`: The identification result:
|
|
@@ -177,8 +204,8 @@ TypeScript >= 4.7 is supported.
|
|
|
177
204
|
|
|
178
205
|
The following runtimes are supported:
|
|
179
206
|
|
|
180
|
-
- Node.js 18 LTS or later ([non-EOL](https://endoflife.date/nodejs))
|
|
207
|
+
- Node.js 18 LTS or later ([non-EOL](https://endoflife.date/nodejs))
|
|
181
208
|
|
|
182
209
|
## Support
|
|
183
210
|
|
|
184
|
-
Please [open an issue](https://github.com/knownagents/node-sdk/issues) with questions, bugs, or suggestions.
|
|
211
|
+
Please [open an issue](https://github.com/knownagents/node-sdk/issues) with questions, bugs, or suggestions.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
+
import type { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
3
|
+
import type { AgentType, IdentificationRequest, IdentificationResult, TrackPageviewOrRESTCallOptions, VisitRequest } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Server-side client for Known Agents analytics, robots.txt generation, and agent identification.
|
|
6
|
+
*/
|
|
7
|
+
export declare class KnownAgents {
|
|
8
|
+
private readonly accessToken;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new instance of the Known Agents client.
|
|
11
|
+
*
|
|
12
|
+
* @param accessToken - Your project's access token.
|
|
13
|
+
*/
|
|
14
|
+
constructor(accessToken: string);
|
|
15
|
+
/**
|
|
16
|
+
* Tracks a single non-MCP pageview or REST API call for analytics.
|
|
17
|
+
* The visit is sent after the response finishes so status, headers, and duration are included.
|
|
18
|
+
* This method is non-blocking and handles errors internally.
|
|
19
|
+
*
|
|
20
|
+
* @param request - The incoming Node.js HTTP request.
|
|
21
|
+
* @param response - The outgoing Node.js HTTP response.
|
|
22
|
+
* @param options - Optional ACP or UCP response metadata for REST API calls.
|
|
23
|
+
*/
|
|
24
|
+
trackPageviewOrRESTCall(request: IncomingMessage, response: ServerResponse, options?: TrackPageviewOrRESTCallOptions): void;
|
|
25
|
+
/**
|
|
26
|
+
* Tracks a single MCP call handled by `StreamableHTTPServerTransport`.
|
|
27
|
+
* Call this after connecting the transport and before the transport processes the request so request and response metadata can be captured.
|
|
28
|
+
* Do not use this with transport instances shared across concurrent requests; use `trackVisits()` instead.
|
|
29
|
+
* This method is non-blocking and handles errors internally.
|
|
30
|
+
*
|
|
31
|
+
* @param request - The incoming Node.js HTTP request.
|
|
32
|
+
* @param response - The outgoing Node.js HTTP response.
|
|
33
|
+
* @param transport - The MCP streamable HTTP transport handling the request.
|
|
34
|
+
*/
|
|
35
|
+
trackMCPCall(request: IncomingMessage, response: ServerResponse, transport: StreamableHTTPServerTransport): void;
|
|
36
|
+
/**
|
|
37
|
+
* Tracks multiple visits in a batch for analytics.
|
|
38
|
+
* This method is non-blocking and handles errors internally.
|
|
39
|
+
* Recommended for high-traffic websites - batch visits and send periodically (e.g. every 30 seconds).
|
|
40
|
+
*
|
|
41
|
+
* @param requests - An array of visits.
|
|
42
|
+
*/
|
|
43
|
+
trackVisits(requests: VisitRequest[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Generates a robots.txt file that disallows the specified agent types.
|
|
46
|
+
* The generated file stays up-to-date with all current and future agents in the specified categories.
|
|
47
|
+
* Cache and serve the result at your website's `/robots.txt` endpoint.
|
|
48
|
+
*
|
|
49
|
+
* @param agentTypes - An array of agent types to disallow.
|
|
50
|
+
* @param disallow - The path to disallow (default: "/" for all paths).
|
|
51
|
+
* @returns A promise that resolves to the generated robots.txt content as a string.
|
|
52
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
53
|
+
*/
|
|
54
|
+
generateRobotsTXT(agentTypes: AgentType[], disallow?: string): Promise<string>;
|
|
55
|
+
/**
|
|
56
|
+
* Identifies and verifies a single agent from an HTTP request.
|
|
57
|
+
* Uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods.
|
|
58
|
+
*
|
|
59
|
+
* @param request - The incoming Node.js HTTP request.
|
|
60
|
+
* @returns A promise that resolves to the identification result.
|
|
61
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
62
|
+
*/
|
|
63
|
+
identifyAgent(request: IncomingMessage): Promise<IdentificationResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Identifies and verifies multiple agents from HTTP requests in a batch.
|
|
66
|
+
* Uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods.
|
|
67
|
+
*
|
|
68
|
+
* @param requests - An array of identification requests.
|
|
69
|
+
* @returns A promise that resolves to an array of identification results.
|
|
70
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
71
|
+
*/
|
|
72
|
+
identifyAgents(requests: IdentificationRequest[]): Promise<IdentificationResult[]>;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=KnownAgents.d.ts.map
|
|
@@ -0,0 +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;AAGvG,OAAO,KAAK,EACR,SAAS,EACT,qBAAqB,EACrB,oBAAoB,EACpB,8BAA8B,EAC9B,YAAY,EACf,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC;;;;OAIG;gBACS,WAAW,EAAE,MAAM;IAI/B;;;;;;;;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;IAqEhH;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI;IA6B3C;;;;;;;;;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;CAsB3F"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KnownAgents = void 0;
|
|
4
|
+
const headers_1 = require("./headers");
|
|
5
|
+
const paths_1 = require("./paths");
|
|
6
|
+
/**
|
|
7
|
+
* Server-side client for Known Agents analytics, robots.txt generation, and agent identification.
|
|
8
|
+
*/
|
|
9
|
+
class KnownAgents {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new instance of the Known Agents client.
|
|
12
|
+
*
|
|
13
|
+
* @param accessToken - Your project's access token.
|
|
14
|
+
*/
|
|
15
|
+
constructor(accessToken) {
|
|
16
|
+
this.accessToken = accessToken;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tracks a single non-MCP pageview or REST API call for analytics.
|
|
20
|
+
* The visit is sent after the response finishes so status, headers, and duration are included.
|
|
21
|
+
* This method is non-blocking and handles errors internally.
|
|
22
|
+
*
|
|
23
|
+
* @param request - The incoming Node.js HTTP request.
|
|
24
|
+
* @param response - The outgoing Node.js HTTP response.
|
|
25
|
+
* @param options - Optional ACP or UCP response metadata for REST API calls.
|
|
26
|
+
*/
|
|
27
|
+
trackPageviewOrRESTCall(request, response, options) {
|
|
28
|
+
const isMCPCall = (0, headers_1.getIsMCPCall)(request.headers);
|
|
29
|
+
const path = request.url;
|
|
30
|
+
const method = request.method;
|
|
31
|
+
if (isMCPCall || !path || (0, paths_1.getIsPathExcluded)(path) || !method) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const created = new Date();
|
|
35
|
+
const sendVisit = () => {
|
|
36
|
+
const responseDurationInMilliseconds = Date.now() - created.getTime();
|
|
37
|
+
const visitRequest = {
|
|
38
|
+
request_path: path,
|
|
39
|
+
request_method: method,
|
|
40
|
+
request_headers: request.headers,
|
|
41
|
+
response_status_code: response?.statusCode,
|
|
42
|
+
response_headers: response.getHeaders(),
|
|
43
|
+
response_duration_in_milliseconds: responseDurationInMilliseconds,
|
|
44
|
+
acp_response_body: options?.acpResponseBody,
|
|
45
|
+
ucp_response_body: options?.ucpResponseBody,
|
|
46
|
+
created: created.toISOString()
|
|
47
|
+
};
|
|
48
|
+
this.trackVisits([visitRequest]);
|
|
49
|
+
};
|
|
50
|
+
if (response.writableEnded) {
|
|
51
|
+
sendVisit();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
response.once("finish", sendVisit);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Tracks a single MCP call handled by `StreamableHTTPServerTransport`.
|
|
59
|
+
* Call this after connecting the transport and before the transport processes the request so request and response metadata can be captured.
|
|
60
|
+
* Do not use this with transport instances shared across concurrent requests; use `trackVisits()` instead.
|
|
61
|
+
* This method is non-blocking and handles errors internally.
|
|
62
|
+
*
|
|
63
|
+
* @param request - The incoming Node.js HTTP request.
|
|
64
|
+
* @param response - The outgoing Node.js HTTP response.
|
|
65
|
+
* @param transport - The MCP streamable HTTP transport handling the request.
|
|
66
|
+
*/
|
|
67
|
+
trackMCPCall(request, response, transport) {
|
|
68
|
+
const path = request.url;
|
|
69
|
+
const method = request.method;
|
|
70
|
+
if (!path || (0, paths_1.getIsPathExcluded)(path) || !method) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const created = new Date();
|
|
74
|
+
let mcpRequestBody;
|
|
75
|
+
let mcpResponseBody;
|
|
76
|
+
const originalTransportOnMessage = transport.onmessage;
|
|
77
|
+
transport.onmessage = (message, extra) => {
|
|
78
|
+
mcpRequestBody = message;
|
|
79
|
+
originalTransportOnMessage?.(message, extra);
|
|
80
|
+
};
|
|
81
|
+
const originalTransportSend = transport.send;
|
|
82
|
+
transport.send = async (message, options) => {
|
|
83
|
+
mcpResponseBody = message;
|
|
84
|
+
await originalTransportSend.call(transport, message, options);
|
|
85
|
+
};
|
|
86
|
+
const restoreTransport = () => {
|
|
87
|
+
transport.onmessage = originalTransportOnMessage;
|
|
88
|
+
transport.send = originalTransportSend;
|
|
89
|
+
};
|
|
90
|
+
const sendVisit = () => {
|
|
91
|
+
restoreTransport();
|
|
92
|
+
if (!mcpRequestBody || !mcpResponseBody) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const responseDurationInMilliseconds = Date.now() - created.getTime();
|
|
96
|
+
const mcpResponseResult = mcpResponseBody.result?.structuredContent ?? mcpResponseBody.result;
|
|
97
|
+
const visitRequest = {
|
|
98
|
+
request_path: path,
|
|
99
|
+
request_method: method,
|
|
100
|
+
request_headers: request.headers,
|
|
101
|
+
response_status_code: response?.statusCode,
|
|
102
|
+
response_headers: response.getHeaders(),
|
|
103
|
+
response_duration_in_milliseconds: responseDurationInMilliseconds,
|
|
104
|
+
mcp_request_method: mcpRequestBody.method,
|
|
105
|
+
mcp_request_tool_name: mcpRequestBody.params?.name,
|
|
106
|
+
mcp_request_client_info_name: mcpRequestBody.params?.clientInfo?.name,
|
|
107
|
+
mcp_request_client_info_version: mcpRequestBody.params?.clientInfo?.version,
|
|
108
|
+
mcp_response_result_is_error: mcpResponseBody.result?.isError,
|
|
109
|
+
mcp_response_error_code: mcpResponseBody.error?.code,
|
|
110
|
+
mcp_response_error_message: mcpResponseBody.error?.message,
|
|
111
|
+
acp_response_body: (0, headers_1.getIsACPCall)(request.headers) ? mcpResponseResult : undefined,
|
|
112
|
+
ucp_response_body: (0, headers_1.getIsUCPCall)(request.headers) ? mcpResponseResult : undefined,
|
|
113
|
+
created: created.toISOString()
|
|
114
|
+
};
|
|
115
|
+
this.trackVisits([visitRequest]);
|
|
116
|
+
};
|
|
117
|
+
if (response.writableEnded) {
|
|
118
|
+
sendVisit();
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
response.once("finish", sendVisit);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Tracks multiple visits in a batch for analytics.
|
|
126
|
+
* This method is non-blocking and handles errors internally.
|
|
127
|
+
* Recommended for high-traffic websites - batch visits and send periodically (e.g. every 30 seconds).
|
|
128
|
+
*
|
|
129
|
+
* @param requests - An array of visits.
|
|
130
|
+
*/
|
|
131
|
+
trackVisits(requests) {
|
|
132
|
+
const filteredRequests = requests
|
|
133
|
+
.filter((request) => {
|
|
134
|
+
return !(0, paths_1.getIsPathExcluded)(request.request_path);
|
|
135
|
+
})
|
|
136
|
+
.map((request) => {
|
|
137
|
+
return {
|
|
138
|
+
...request,
|
|
139
|
+
request_headers: (0, headers_1.getFilteredHeaders)(request.request_headers),
|
|
140
|
+
response_headers: request.response_headers ? (0, headers_1.getFilteredHeaders)(request.response_headers) : undefined,
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
if (filteredRequests.length === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
fetch("https://api.knownagents.com/visits", {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
150
|
+
"Content-Type": "application/json"
|
|
151
|
+
},
|
|
152
|
+
body: JSON.stringify(filteredRequests)
|
|
153
|
+
}).catch(error => {
|
|
154
|
+
console.error(`Known Agents failed to track visits: ${error.message}`);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Generates a robots.txt file that disallows the specified agent types.
|
|
159
|
+
* The generated file stays up-to-date with all current and future agents in the specified categories.
|
|
160
|
+
* Cache and serve the result at your website's `/robots.txt` endpoint.
|
|
161
|
+
*
|
|
162
|
+
* @param agentTypes - An array of agent types to disallow.
|
|
163
|
+
* @param disallow - The path to disallow (default: "/" for all paths).
|
|
164
|
+
* @returns A promise that resolves to the generated robots.txt content as a string.
|
|
165
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
166
|
+
*/
|
|
167
|
+
async generateRobotsTXT(agentTypes, disallow = "/") {
|
|
168
|
+
const response = await fetch("https://api.knownagents.com/robots-txts", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
172
|
+
"Content-Type": "application/json"
|
|
173
|
+
},
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
agent_types: agentTypes,
|
|
176
|
+
disallow: disallow
|
|
177
|
+
})
|
|
178
|
+
});
|
|
179
|
+
if (response.ok) {
|
|
180
|
+
return await response.text();
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw new Error(`Known Agents failed to generate robots.txt: ${response.status} ${response.statusText}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Identifies and verifies a single agent from an HTTP request.
|
|
188
|
+
* Uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods.
|
|
189
|
+
*
|
|
190
|
+
* @param request - The incoming Node.js HTTP request.
|
|
191
|
+
* @returns A promise that resolves to the identification result.
|
|
192
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
193
|
+
*/
|
|
194
|
+
async identifyAgent(request) {
|
|
195
|
+
const response = await fetch("https://api.knownagents.com/agent-identifications", {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
199
|
+
"Content-Type": "application/json"
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
request_headers: (0, headers_1.getFilteredHeaders)(request.headers)
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
if (response.ok) {
|
|
206
|
+
return await response.json();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
throw new Error(`Known Agents failed to identify agent: ${response.status} ${response.statusText}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Identifies and verifies multiple agents from HTTP requests in a batch.
|
|
214
|
+
* Uses Web Bot Auth (HTTP message signatures), IP matching, or other available methods.
|
|
215
|
+
*
|
|
216
|
+
* @param requests - An array of identification requests.
|
|
217
|
+
* @returns A promise that resolves to an array of identification results.
|
|
218
|
+
* @throws {Error} If the API call fails or returns a non-200 status.
|
|
219
|
+
*/
|
|
220
|
+
async identifyAgents(requests) {
|
|
221
|
+
const response = await fetch("https://api.knownagents.com/agent-identifications", {
|
|
222
|
+
method: "POST",
|
|
223
|
+
headers: {
|
|
224
|
+
"Authorization": `Bearer ${this.accessToken}`,
|
|
225
|
+
"Content-Type": "application/json"
|
|
226
|
+
},
|
|
227
|
+
body: JSON.stringify(requests.map(request => {
|
|
228
|
+
return {
|
|
229
|
+
...request,
|
|
230
|
+
request_headers: (0, headers_1.getFilteredHeaders)(request.request_headers),
|
|
231
|
+
};
|
|
232
|
+
}))
|
|
233
|
+
});
|
|
234
|
+
if (response.ok) {
|
|
235
|
+
return await response.json();
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
throw new Error(`Known Agents failed to identify agents: ${response.status} ${response.statusText}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.KnownAgents = KnownAgents;
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders, OutgoingHttpHeaders } from "http";
|
|
2
|
+
export declare function getFilteredHeaders<Headers extends IncomingHttpHeaders | OutgoingHttpHeaders>(headers: Headers): Headers;
|
|
3
|
+
export declare function getIsMCPCall(headers: IncomingHttpHeaders): boolean;
|
|
4
|
+
export declare function getIsUCPCall(headers: IncomingHttpHeaders): boolean;
|
|
5
|
+
export declare function getIsACPCall(headers: IncomingHttpHeaders): boolean;
|
|
6
|
+
//# sourceMappingURL=headers.d.ts.map
|
|
@@ -0,0 +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,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAIlE"}
|