mppx 0.2.5 → 0.3.1

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.
@@ -38,10 +38,14 @@ export declare namespace create {
38
38
  type Config = {
39
39
  /** Base path prefix to strip before routing (e.g. `'/api/proxy'`). */
40
40
  basePath?: string | undefined;
41
+ /** Short description of the proxy shown in `llms.txt`. */
42
+ description?: string | undefined;
41
43
  /** Custom `fetch` implementation. Defaults to `globalThis.fetch`. */
42
44
  fetch?: typeof globalThis.fetch | undefined;
43
45
  /** Services to proxy. Each service is mounted at `/{serviceId}/`. */
44
46
  services: Service.Service[];
47
+ /** Human-readable title for the proxy shown in `llms.txt`. */
48
+ title?: string | undefined;
45
49
  };
46
50
  }
47
51
  //# sourceMappingURL=Proxy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/Proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAA;AAKtC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC,kFAAkF;AAClF,MAAM,MAAM,KAAK,GAAG;IAClB,sFAAsF;IACtF,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,uFAAuF;IACvF,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CAAA;CACxE,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK,CAgFnD;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG;QACnB,sEAAsE;QACtE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC7B,qEAAqE;QACrE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,GAAG,SAAS,CAAA;QAC3C,qEAAqE;QACrE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;KAC5B,CAAA;CACF"}
1
+ {"version":3,"file":"Proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/Proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAA;AAKtC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC,kFAAkF;AAClF,MAAM,MAAM,KAAK,GAAG;IAClB,sFAAsF;IACtF,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,uFAAuF;IACvF,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CAAA;CACxE,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK,CA6HnD;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG;QACnB,sEAAsE;QACtE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC7B,0DAA0D;QAC1D,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAChC,qEAAqE;QACrE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,GAAG,SAAS,CAAA;QAC3C,qEAAqE;QACrE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;QAC3B,8DAA8D;QAC9D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC3B,CAAA;CACF"}
@@ -45,17 +45,47 @@ export function create(config) {
45
45
  if (!pathname)
46
46
  return new Response('Not Found', { status: 404 });
47
47
  if (request.method === 'GET' && pathname === '/llms.txt')
48
- return new Response(Service.toLlmsTxt(config.services), {
49
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
50
- });
51
- if (request.method === 'GET' && (pathname === '/services' || pathname === '/services/'))
48
+ return new Response(Service.toLlmsTxt(config.services, {
49
+ title: config.title,
50
+ description: config.description,
51
+ }), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
52
+ if (request.method === 'GET' && pathname === '/discover.md')
53
+ return new Response(Service.toLlmsTxt(config.services, {
54
+ title: config.title,
55
+ description: config.description,
56
+ }), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
57
+ if (request.method === 'GET' && (pathname === '/discover' || pathname === '/discover/')) {
58
+ if (wantsMarkdown(request))
59
+ return new Response(Service.toLlmsTxt(config.services, {
60
+ title: config.title,
61
+ description: config.description,
62
+ }), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
63
+ return Response.json(config.services.map(Service.serialize));
64
+ }
65
+ if (request.method === 'GET' &&
66
+ (pathname === '/discover/all' || pathname === '/discover/all/')) {
67
+ if (wantsMarkdown(request))
68
+ return new Response(Service.toServicesMarkdown(config.services), {
69
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
70
+ });
52
71
  return Response.json(config.services.map(Service.serialize));
72
+ }
73
+ if (request.method === 'GET' && pathname === '/discover/all.md')
74
+ return new Response(Service.toServicesMarkdown(config.services), {
75
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
76
+ });
53
77
  {
54
- const match = pathname.match(/^\/services\/([^/]+)\/?$/);
78
+ // List service
79
+ const match = pathname.match(/^\/discover\/([^/]+)\.md$/) ?? pathname.match(/^\/discover\/([^/]+)\/?$/);
55
80
  if (request.method === 'GET' && match) {
56
81
  const service = config.services.find((s) => s.id === match[1]);
57
82
  if (!service)
58
83
  return new Response('Not Found', { status: 404 });
84
+ const wantsText = pathname.endsWith('.md') || wantsMarkdown(request);
85
+ if (wantsText)
86
+ return new Response(Service.toMarkdown(service), {
87
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
88
+ });
59
89
  return Response.json(Service.serialize(service));
60
90
  }
61
91
  }
@@ -123,4 +153,43 @@ async function proxyUpstream(options) {
123
153
  upstreamRes = await service.rewriteResponse(upstreamRes, ctx);
124
154
  return upstreamRes;
125
155
  }
156
+ const aiUserAgents = [
157
+ 'GPTBot',
158
+ 'OAI-SearchBot',
159
+ 'ChatGPT-User',
160
+ 'anthropic-ai',
161
+ 'ClaudeBot',
162
+ 'claude-web',
163
+ 'PerplexityBot',
164
+ 'Perplexity-User',
165
+ 'Google-Extended',
166
+ 'Googlebot',
167
+ 'Bingbot',
168
+ 'Amazonbot',
169
+ 'Applebot',
170
+ 'Applebot-Extended',
171
+ 'FacebookBot',
172
+ 'meta-externalagent',
173
+ 'Bytespider',
174
+ 'DuckAssistBot',
175
+ 'cohere-ai',
176
+ 'AI2Bot',
177
+ 'CCBot',
178
+ 'Diffbot',
179
+ 'YouBot',
180
+ 'MistralAI-User',
181
+ 'GoogleAgent-Mariner',
182
+ ];
183
+ const terminalUserAgents = ['curl', 'Wget', 'HTTPie', 'httpie-go', 'mppx', 'presto', 'xh'];
184
+ function wantsMarkdown(request) {
185
+ const accept = request.headers.get('accept');
186
+ if (accept && (accept.includes('text/markdown') || accept.includes('text/plain')))
187
+ return true;
188
+ const ua = request.headers.get('user-agent') ?? '';
189
+ if (aiUserAgents.some((agent) => ua.includes(agent)))
190
+ return true;
191
+ if (terminalUserAgents.some((agent) => ua.includes(agent)))
192
+ return true;
193
+ return false;
194
+ }
126
195
  //# sourceMappingURL=Proxy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Proxy.js","sourceRoot":"","sources":["../../src/proxy/Proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Proxy.js","sourceRoot":"","sources":["../../src/proxy/Proxy.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,23 @@
1
1
  /** A proxied upstream service with route definitions and optional request/response hooks. */
2
2
  export type Service = {
3
- /** Unique identifier used as the URL prefix (e.g. `'openai'` → `/{id}/...`). */
4
- id: string;
5
3
  /** Base URL of the upstream service (e.g. `'https://api.openai.com'`). */
6
4
  baseUrl: string;
7
- /** Map of route patterns to endpoint handlers. */
8
- routes: EndpointMap;
5
+ /** Short description of the service. */
6
+ description?: string | undefined;
7
+ /** Unique identifier used as the URL prefix (e.g. `'openai'` → `/{id}/...`). */
8
+ id: string;
9
+ /** Returns a documentation URL. Called with no argument for the service root, or with a route pattern for per-endpoint docs. */
10
+ docsLlmsUrl?: ((options: {
11
+ route?: string | undefined;
12
+ }) => string | undefined) | undefined;
9
13
  /** Hook to modify the upstream request before sending (e.g. inject auth headers). */
10
14
  rewriteRequest?: ((req: Request, ctx: Context) => Request | Promise<Request>) | undefined;
11
15
  /** Hook to modify the upstream response before returning to the client. */
12
16
  rewriteResponse?: ((res: Response, ctx: Context) => Response | Promise<Response>) | undefined;
17
+ /** Map of route patterns to endpoint handlers. */
18
+ routes: EndpointMap;
19
+ /** Human-readable title for the service (e.g. `'OpenAI'`). */
20
+ title?: string | undefined;
13
21
  };
14
22
  /**
15
23
  * An endpoint definition.
@@ -71,30 +79,49 @@ export declare namespace from {
71
79
  baseUrl: string;
72
80
  /** Shorthand: inject `Authorization: Bearer {token}` header. */
73
81
  bearer?: string | undefined;
82
+ /** Short description of the service. */
83
+ description?: string | undefined;
74
84
  /** Shorthand: inject custom headers. */
75
85
  headers?: Record<string, string> | undefined;
86
+ /** Documentation URL for the service. String for a static base URL, or a function receiving an optional endpoint pattern. */
87
+ docsLlmsUrl?: string | ((options: {
88
+ route?: string | undefined;
89
+ }) => string | undefined) | undefined;
76
90
  /** Shorthand: full request mutation function. Takes priority over `bearer`/`headers`. */
77
91
  mutate?: ((req: Request) => Request | Promise<Request>) | undefined;
78
92
  /** Hook to modify the upstream request. Receives typed per-endpoint options via `ctx`. */
79
93
  rewriteRequest?: ((req: Request, ctx: Context & Partial<options & {}>) => Request | Promise<Request>) | undefined;
80
94
  /** Map of route patterns to endpoint definitions. */
81
95
  routes: EndpointMap;
96
+ /** Human-readable title for the service. */
97
+ title?: string | undefined;
82
98
  };
83
99
  }
84
100
  export { from as custom };
85
101
  /** Serializes a service for discovery responses. */
86
102
  export declare function serialize(s: Service): {
87
- id: string;
88
103
  baseUrl: string;
104
+ description: string | undefined;
105
+ id: string;
106
+ docsLlmsUrl: string | undefined;
89
107
  routes: {
108
+ docsLlmsUrl: string | undefined;
90
109
  method: string | undefined;
91
110
  path: string;
92
111
  pattern: string;
93
112
  payment: Record<string, unknown> | null;
94
113
  }[];
114
+ title: string | undefined;
95
115
  };
96
116
  /** Renders an llms.txt markdown string for a list of services. */
97
- export declare function toLlmsTxt(services: Service[]): string;
117
+ export declare function toLlmsTxt(services: Service[], options?: {
118
+ title?: string | undefined;
119
+ description?: string | undefined;
120
+ }): string;
121
+ /** Renders a full markdown listing of all services with their routes. */
122
+ export declare function toServicesMarkdown(services: Service[]): string;
123
+ /** Renders a markdown string for a single service. */
124
+ export declare function toMarkdown(s: Service): string;
98
125
  /** Extracts per-endpoint options from an endpoint definition. */
99
126
  export declare function getOptions(endpoint: Endpoint): EndpointOptions | undefined;
100
127
  //# sourceMappingURL=Service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/proxy/Service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/proxy/Service.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,MAAM,MAAM,OAAO,GAAG;IACpB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAA;IACV,gIAAgI;IAChI,WAAW,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAA;IAC3F,qFAAqF;IACrF,cAAc,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAA;IACzF,2EAA2E;IAC3E,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAA;IAC7F,kDAAkD;IAClD,MAAM,EAAE,WAAW,CAAA;IACnB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG;IAAE,GAAG,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAA;AAE9F,+DAA+D;AAC/D,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GACzF,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;AAE/B,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;AAErE,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GACpB;IAAE,SAAS,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAA;CAAE,CAAA;AAE5E,sGAAsG;AACtG,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,eAAe,CAAA;AAEnB,MAAM,MAAM,IAAI,CACd,OAAO,SAAS;IACd,MAAM,EAAE,MAAM,CAAA;CACf,IACC;IACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;CACvC,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AAE3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAkBzF;AAED,MAAM,CAAC,OAAO,WAAW,IAAI,CAAC;IAC5B,KAAY,MAAM,CAAC,OAAO,GAAG,OAAO,IAAI;QACtC,wCAAwC;QACxC,OAAO,EAAE,MAAM,CAAA;QACf,gEAAgE;QAChE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC3B,wCAAwC;QACxC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAChC,wCAAwC;QACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;QAC5C,6HAA6H;QAC7H,WAAW,CAAC,EACR,MAAM,GACN,CAAC,CAAC,OAAO,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAAE,KAAK,MAAM,GAAG,SAAS,CAAC,GACjE,SAAS,CAAA;QACb,yFAAyF;QACzF,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAA;QACnE,0FAA0F;QAC1F,cAAc,CAAC,EACX,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GACpF,SAAS,CAAA;QACb,qDAAqD;QACrD,MAAM,EAAE,WAAW,CAAA;QACnB,4CAA4C;QAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC3B,CAAA;CACF;AAED,OAAO,EAAE,IAAI,IAAI,MAAM,EAAE,CAAA;AAiCzB,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO;;;;;;;;;;;;;EAmBnC;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CACvB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzE,MAAM,CAmBR;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAY9D;AAED,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAO7C;AA6BD,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAI1E"}
@@ -17,9 +17,12 @@ import { Value } from 'ox';
17
17
  export function from(id, config) {
18
18
  const rewriteFromConfig = resolveRewriteRequest(config);
19
19
  return {
20
- id,
21
20
  baseUrl: config.baseUrl,
21
+ description: config.description,
22
+ id,
23
+ docsLlmsUrl: resolveLlmsUrl(config.docsLlmsUrl),
22
24
  routes: config.routes,
25
+ title: config.title,
23
26
  rewriteRequest: config.rewriteRequest
24
27
  ? rewriteFromConfig
25
28
  ? async (req, ctx) => {
@@ -63,66 +66,96 @@ function resolveRewriteRequest(config) {
63
66
  /** Serializes a service for discovery responses. */
64
67
  export function serialize(s) {
65
68
  return {
66
- id: s.id,
67
69
  baseUrl: s.baseUrl,
70
+ description: s.description,
71
+ id: s.id,
72
+ docsLlmsUrl: s.docsLlmsUrl?.({}),
68
73
  routes: Object.entries(s.routes).map(([pattern, endpoint]) => {
69
74
  const tokens = pattern.trim().split(/\s+/);
70
75
  const hasMethod = tokens.length >= 2;
71
76
  return {
77
+ docsLlmsUrl: s.docsLlmsUrl?.({ route: pattern }),
72
78
  method: hasMethod ? tokens[0] : undefined,
73
79
  path: hasMethod ? tokens.slice(1).join(' ') : tokens[0],
74
80
  pattern,
75
81
  payment: endpoint ? resolvePayment(endpoint) : null,
76
82
  };
77
83
  }),
84
+ title: s.title,
78
85
  };
79
86
  }
80
87
  /** Renders an llms.txt markdown string for a list of services. */
81
- export function toLlmsTxt(services) {
88
+ export function toLlmsTxt(services, options) {
82
89
  const lines = [
83
- '# API Proxy',
84
- '',
85
- '> Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).',
90
+ `# ${options?.title ?? 'API Proxy'}`,
86
91
  '',
87
- 'For machine-readable service data, use `GET /services` (JSON).',
92
+ `> ${options?.description ?? 'Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).'}`,
88
93
  '',
89
94
  ];
90
95
  if (services.length === 0)
91
96
  return lines.join('\n');
92
97
  lines.push('## Services', '');
93
98
  for (const s of services) {
94
- const serialized = serialize(s);
95
- const free = serialized.routes.filter((r) => r.payment === null).length;
96
- const paid = serialized.routes.length - free;
97
- const parts = [paid && `${paid} paid`, free && `${free} free`].filter(Boolean).join(', ');
98
- lines.push(`- [${s.id}](${s.baseUrl}): ${parts}`);
99
+ const label = s.title ?? s.id;
100
+ const desc = s.description ? `: ${s.description}` : '';
101
+ lines.push(`- [${label}](/discover/${s.id}.md)${desc}`);
99
102
  }
103
+ lines.push('', '[See all service definitions](/discover/all.md)');
104
+ return lines.join('\n');
105
+ }
106
+ /** Renders a full markdown listing of all services with their routes. */
107
+ export function toServicesMarkdown(services) {
108
+ const lines = ['# Services', ''];
109
+ if (services.length === 0)
110
+ return lines.join('\n');
100
111
  for (const s of services) {
101
- const serialized = serialize(s);
102
- lines.push('', `## ${s.id}`, '');
103
- for (const route of serialized.routes) {
104
- if (!route.payment) {
105
- lines.push(`- \`${route.pattern}\`: Free`);
106
- continue;
107
- }
108
- const p = route.payment;
109
- const parts = [`${p.intent}`];
112
+ lines.push(`## [${s.title ?? s.id}](/discover/${s.id}.md)`, '');
113
+ if (s.description)
114
+ lines.push(s.description, '');
115
+ pushRoutes(lines, s);
116
+ }
117
+ return lines.join('\n');
118
+ }
119
+ /** Renders a markdown string for a single service. */
120
+ export function toMarkdown(s) {
121
+ const docsLlmsUrl = s.docsLlmsUrl?.({});
122
+ const lines = [`# ${s.title ?? s.id}`, ''];
123
+ if (docsLlmsUrl)
124
+ lines.push(`> Documentation: ${docsLlmsUrl}`, '');
125
+ if (s.description)
126
+ lines.push(s.description, '');
127
+ pushRoutes(lines, s, '##');
128
+ return lines.join('\n');
129
+ }
130
+ function pushRoutes(lines, s, heading = '###') {
131
+ lines.push(`${heading} Routes`, '');
132
+ const serialized = serialize(s);
133
+ for (const route of serialized.routes) {
134
+ const p = route.payment;
135
+ const desc = p?.description ? `: ${p.description}` : '';
136
+ lines.push(`- \`${route.pattern}\`${desc}`);
137
+ if (!p) {
138
+ lines.push(' - Type: free');
139
+ }
140
+ else {
141
+ lines.push(` - Type: ${p.intent}`);
110
142
  if (p.amount) {
111
- const unit = `${p.amount} units`;
112
- parts.push(p.unitType ? `${unit} per ${p.unitType}` : unit);
143
+ const perUnit = p.unitType ? `/${p.unitType}` : '';
144
+ if (p.decimals !== undefined) {
145
+ const price = Number(p.amount) / 10 ** Number(p.decimals);
146
+ lines.push(` - Price: ${price}${perUnit} (${p.amount} units, ${p.decimals} decimals)`);
147
+ }
148
+ else {
149
+ lines.push(` - Units: ${p.amount}${perUnit}`);
150
+ }
113
151
  }
114
- if (p.description)
115
- parts.push(`"${p.description}"`);
116
- const meta = [
117
- p.currency && `currency: ${p.currency}`,
118
- p.decimals !== undefined && `decimals: ${p.decimals}`,
119
- ].filter(Boolean);
120
- if (meta.length)
121
- parts.push(`(${meta.join(', ')})`);
122
- lines.push(`- \`${route.pattern}\`: ${parts.join(' — ')}`);
152
+ if (p.currency)
153
+ lines.push(` - Currency: ${p.currency}`);
123
154
  }
155
+ if (route.docsLlmsUrl)
156
+ lines.push(` - Docs: ${route.docsLlmsUrl}`);
157
+ lines.push('');
124
158
  }
125
- return lines.join('\n');
126
159
  }
127
160
  /** Extracts per-endpoint options from an endpoint definition. */
128
161
  export function getOptions(endpoint) {
@@ -144,4 +177,11 @@ function resolvePayment(endpoint) {
144
177
  })();
145
178
  return { intent, method: name, ...rest, ...(amount !== undefined && { amount }) };
146
179
  }
180
+ function resolveLlmsUrl(input) {
181
+ if (!input)
182
+ return undefined;
183
+ if (typeof input === 'function')
184
+ return input;
185
+ return ({ route }) => (route ? undefined : input);
186
+ }
147
187
  //# sourceMappingURL=Service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/proxy/Service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/proxy/Service.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,mBAUjD;AAED,MAAM,CAAC,OAAO,WAAW,SAAS,CAAC;IACjC,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,qDAAqD;QACrD,MAAM,EAAE,MAAM,CAAA;QACd,oEAAoE;QACpE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,MAAM,EACF,mBAAmB,GACnB,2BAA2B,GAC3B,0BAA0B,GAC1B,mCAAmC,GACnC,mBAAmB,CAAA;KACxB,CAAC,CAAA;CACH"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,mBAYjD;AAED,MAAM,CAAC,OAAO,WAAW,SAAS,CAAC;IACjC,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,qDAAqD;QACrD,MAAM,EAAE,MAAM,CAAA;QACd,oEAAoE;QACpE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,MAAM,EACF,mBAAmB,GACnB,2BAA2B,GAC3B,0BAA0B,GAC1B,mCAAmC,GACnC,mBAAmB,CAAA;KACxB,CAAC,CAAA;CACH"}
@@ -19,12 +19,14 @@ import * as Service from '../Service.js';
19
19
  export function anthropic(config) {
20
20
  return Service.from('anthropic', {
21
21
  baseUrl: config.baseUrl ?? 'https://api.anthropic.com',
22
+ description: 'Claude language models for messages and completions.',
22
23
  rewriteRequest(request, ctx) {
23
24
  const apiKey = ctx.apiKey ?? config.apiKey;
24
25
  request.headers.set('x-api-key', apiKey);
25
26
  return request;
26
27
  },
27
28
  routes: config.routes,
29
+ title: 'Anthropic',
28
30
  });
29
31
  }
30
32
  //# sourceMappingURL=anthropic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/proxy/services/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,MAAwB;IAChD,OAAO,OAAO,CAAC,IAAI,CAAmB,WAAW,EAAE;QACjD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,2BAA2B;QACtD,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;YACxC,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/proxy/services/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,MAAwB;IAChD,OAAO,OAAO,CAAC,IAAI,CAAmB,WAAW,EAAE;QACjD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,2BAA2B;QACtD,WAAW,EAAE,sDAAsD;QACnE,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;YACxC,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,WAAW;KACnB,CAAC,CAAA;AACJ,CAAC"}
@@ -23,6 +23,7 @@ export declare namespace openai {
23
23
  apiKey: string;
24
24
  /** Base URL override. Defaults to `'https://api.openai.com'`. */
25
25
  baseUrl?: string | undefined;
26
+ /** Route definitions for OpenAI endpoints. */
26
27
  routes: 'POST /v1/chat/completions' | 'POST /v1/completions' | 'POST /v1/embeddings' | 'POST /v1/images/generations' | 'POST /v1/images/edits' | 'POST /v1/images/variations' | 'POST /v1/audio/transcriptions' | 'POST /v1/audio/translations';
27
28
  }>;
28
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,mBAU3C;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,8DAA8D;QAC9D,MAAM,EAAE,MAAM,CAAA;QACd,iEAAiE;QACjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,MAAM,EACF,2BAA2B,GAC3B,sBAAsB,GACtB,qBAAqB,GACrB,6BAA6B,GAC7B,uBAAuB,GACvB,4BAA4B,GAC5B,+BAA+B,GAC/B,6BAA6B,CAAA;KAClC,CAAC,CAAA;CACH"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,mBAgB3C;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,8DAA8D;QAC9D,MAAM,EAAE,MAAM,CAAA;QACd,iEAAiE;QACjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,8CAA8C;QAC9C,MAAM,EACF,2BAA2B,GAC3B,sBAAsB,GACtB,qBAAqB,GACrB,6BAA6B,GAC7B,uBAAuB,GACvB,4BAA4B,GAC5B,+BAA+B,GAC/B,6BAA6B,CAAA;KAClC,CAAC,CAAA;CACH"}
@@ -19,12 +19,17 @@ import * as Service from '../Service.js';
19
19
  export function openai(config) {
20
20
  return Service.from('openai', {
21
21
  baseUrl: config.baseUrl ?? 'https://api.openai.com',
22
+ description: 'Chat completions, embeddings, image generation, and audio transcription.',
23
+ docsLlmsUrl: ({ route }) => route
24
+ ? `https://context7.com/websites/platform_openai/llms.txt?topic=${encodeURIComponent(route)}`
25
+ : 'https://context7.com/websites/platform_openai/llms.txt',
22
26
  rewriteRequest(request, ctx) {
23
27
  const apiKey = ctx.apiKey ?? config.apiKey;
24
28
  request.headers.set('Authorization', `Bearer ${apiKey}`);
25
29
  return request;
26
30
  },
27
31
  routes: config.routes,
32
+ title: 'OpenAI',
28
33
  });
29
34
  }
30
35
  //# sourceMappingURL=openai.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/proxy/services/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CAAC,MAAqB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAgB,QAAQ,EAAE;QAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,wBAAwB;QACnD,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,MAAM,EAAE,CAAC,CAAA;YACxD,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/proxy/services/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CAAC,MAAqB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAgB,QAAQ,EAAE;QAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,wBAAwB;QACnD,WAAW,EAAE,0EAA0E;QACvF,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACzB,KAAK;YACH,CAAC,CAAC,gEAAgE,kBAAkB,CAAC,KAAK,CAAC,EAAE;YAC7F,CAAC,CAAC,wDAAwD;QAC9D,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,MAAM,EAAE,CAAC,CAAA;YACxD,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAA;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,mBAU3C;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,mDAAmD;QACnD,MAAM,EAAE,MAAM,CAAA;QACd,iEAAiE;QACjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,MAAM,EACF,kBAAkB,GAClB,oBAAoB,GACpB,uBAAuB,GACvB,0BAA0B,GAC1B,6BAA6B,GAC7B,wBAAwB,GACxB,2BAA2B,GAC3B,mBAAmB,GACnB,sBAAsB,CAAA;KAC3B,CAAC,CAAA;CACH"}
1
+ {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../src/proxy/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,mBAgB3C;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,mDAAmD;QACnD,MAAM,EAAE,MAAM,CAAA;QACd,iEAAiE;QACjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,MAAM,EACF,kBAAkB,GAClB,oBAAoB,GACpB,uBAAuB,GACvB,0BAA0B,GAC1B,6BAA6B,GAC7B,wBAAwB,GACxB,2BAA2B,GAC3B,mBAAmB,GACnB,sBAAsB,CAAA;KAC3B,CAAC,CAAA;CACH"}
@@ -19,12 +19,17 @@ import * as Service from '../Service.js';
19
19
  export function stripe(config) {
20
20
  return Service.from('stripe', {
21
21
  baseUrl: config.baseUrl ?? 'https://api.stripe.com',
22
+ description: 'Payment processing, customers, subscriptions, and invoices.',
23
+ docsLlmsUrl: ({ route }) => route
24
+ ? `https://context7.com/websites/stripe/llms.txt?topic=${encodeURIComponent(route)}`
25
+ : 'https://docs.stripe.com/llms.txt',
22
26
  rewriteRequest(request, ctx) {
23
27
  const apiKey = ctx.apiKey ?? config.apiKey;
24
28
  request.headers.set('Authorization', `Basic ${btoa(`${apiKey}:`)}`);
25
29
  return request;
26
30
  },
27
31
  routes: config.routes,
32
+ title: 'Stripe',
28
33
  });
29
34
  }
30
35
  //# sourceMappingURL=stripe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/proxy/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CAAC,MAAqB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAgB,QAAQ,EAAE;QAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,wBAAwB;QACnD,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,CAAA;YACnE,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/proxy/services/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CAAC,MAAqB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAgB,QAAQ,EAAE;QAC3C,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,wBAAwB;QACnD,WAAW,EAAE,6DAA6D;QAC1E,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACzB,KAAK;YACH,CAAC,CAAC,uDAAuD,kBAAkB,CAAC,KAAK,CAAC,EAAE;YACpF,CAAC,CAAC,kCAAkC;QACxC,cAAc,CAAC,OAAO,EAAE,GAAG;YACzB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAA;YAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,CAAA;YACnE,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAA;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mppx",
3
3
  "type": "module",
4
- "version": "0.2.5",
4
+ "version": "0.3.1",
5
5
  "main": "./dist/index.js",
6
6
  "license": "MIT",
7
7
  "files": [
@@ -61,7 +61,7 @@ function createUpstream(handler: (req: Request) => Response | Promise<Response>)
61
61
  }
62
62
 
63
63
  describe('create', () => {
64
- test('behavior: GET /services returns service discovery', async () => {
64
+ test('behavior: GET /discover/all returns service discovery JSON', async () => {
65
65
  const proxy = ApiProxy.create({
66
66
  services: [
67
67
  Service.from('api', {
@@ -80,7 +80,7 @@ describe('create', () => {
80
80
  })
81
81
  proxyServer = await Http.createServer(proxy.listener)
82
82
 
83
- const res = await fetch(`${proxyServer.url}/services`)
83
+ const res = await fetch(`${proxyServer.url}/discover/all`)
84
84
  expect(res.status).toBe(200)
85
85
  expect(await res.json()).toMatchInlineSnapshot(`
86
86
  [
@@ -129,8 +129,28 @@ describe('create', () => {
129
129
  `)
130
130
  })
131
131
 
132
- test('behavior: GET /llms.txt returns markdown', async () => {
132
+ test('behavior: GET /discover returns JSON by default', async () => {
133
133
  const proxy = ApiProxy.create({
134
+ services: [
135
+ Service.from('api', {
136
+ baseUrl: 'https://api.example.com',
137
+ routes: {
138
+ 'GET /v1/models': true,
139
+ },
140
+ }),
141
+ ],
142
+ })
143
+ proxyServer = await Http.createServer(proxy.listener)
144
+
145
+ const res = await fetch(`${proxyServer.url}/discover`)
146
+ expect(res.status).toBe(200)
147
+ expect(res.headers.get('content-type')).toMatchInlineSnapshot(`"application/json"`)
148
+ })
149
+
150
+ test('behavior: GET /discover returns llms.txt for markdown clients', async () => {
151
+ const proxy = ApiProxy.create({
152
+ title: 'My AI Gateway',
153
+ description: 'A paid proxy for LLM and AI services.',
134
154
  services: [
135
155
  openai({
136
156
  apiKey: 'sk-test',
@@ -163,34 +183,26 @@ describe('create', () => {
163
183
  })
164
184
  proxyServer = await Http.createServer(proxy.listener)
165
185
 
166
- const res = await fetch(`${proxyServer.url}/llms.txt`)
186
+ const res = await fetch(`${proxyServer.url}/discover`, {
187
+ headers: { Accept: 'text/plain' },
188
+ })
167
189
  expect(res.status).toBe(200)
168
190
  expect(res.headers.get('content-type')).toBe('text/plain; charset=utf-8')
169
191
  expect(await res.text()).toMatchInlineSnapshot(`
170
- "# API Proxy
171
-
172
- > Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).
192
+ "# My AI Gateway
173
193
 
174
- For machine-readable service data, use \`GET /services\` (JSON).
194
+ > A paid proxy for LLM and AI services.
175
195
 
176
196
  ## Services
177
197
 
178
- - [openai](https://api.openai.com): 2 paid
179
- - [anthropic](https://api.anthropic.com): 2 paid
198
+ - [OpenAI](/discover/openai.md): Chat completions, embeddings, image generation, and audio transcription.
199
+ - [Anthropic](/discover/anthropic.md): Claude language models for messages and completions.
180
200
 
181
- ## openai
182
-
183
- - \`POST /v1/chat/completions\`: charge — 50000 units — "Chat completion" — (currency: 0x20c0000000000000000000000000000000000001, decimals: 6)
184
- - \`POST /v1/embeddings\`: charge — 10000 units — "Generate embeddings" — (currency: 0x20c0000000000000000000000000000000000001, decimals: 6)
185
-
186
- ## anthropic
187
-
188
- - \`POST /v1/messages\`: charge — 30000 units — "Send message" — (currency: 0x20c0000000000000000000000000000000000001, decimals: 6)
189
- - \`POST /v1/messages/stream\`: session — 10000 units per token — "Stream message" — (currency: 0x20c0000000000000000000000000000000000001, decimals: 6)"
201
+ [See all service definitions](/discover/all.md)"
190
202
  `)
191
203
  })
192
204
 
193
- test('behavior: GET /services/:id returns single service', async () => {
205
+ test('behavior: GET /discover/:id returns single service', async () => {
194
206
  const proxy = ApiProxy.create({
195
207
  services: [
196
208
  Service.from('api', {
@@ -204,7 +216,7 @@ describe('create', () => {
204
216
  })
205
217
  proxyServer = await Http.createServer(proxy.listener)
206
218
 
207
- const res = await fetch(`${proxyServer.url}/services/api`)
219
+ const res = await fetch(`${proxyServer.url}/discover/api`)
208
220
  expect(res.status).toBe(200)
209
221
  expect(await res.json()).toMatchInlineSnapshot(`
210
222
  {
@@ -236,10 +248,173 @@ describe('create', () => {
236
248
  `)
237
249
  })
238
250
 
239
- test('behavior: GET /services/:id returns 404 for unknown', async () => {
251
+ test('behavior: GET /discover/all.md returns full markdown with routes', async () => {
252
+ const proxy = ApiProxy.create({
253
+ services: [
254
+ openai({
255
+ apiKey: 'sk-test',
256
+ routes: {
257
+ 'POST /v1/chat/completions': mppx_server.charge({
258
+ amount: '0.05',
259
+ description: 'Chat completion',
260
+ }),
261
+ 'GET /v1/models': true,
262
+ },
263
+ }),
264
+ anthropic({
265
+ apiKey: 'sk-ant-test',
266
+ routes: {
267
+ 'POST /v1/messages': mppx_server.charge({
268
+ amount: '0.03',
269
+ description: 'Send message',
270
+ }),
271
+ },
272
+ }),
273
+ ],
274
+ })
275
+ proxyServer = await Http.createServer(proxy.listener)
276
+
277
+ const res = await fetch(`${proxyServer.url}/discover/all.md`)
278
+ expect(res.status).toBe(200)
279
+ expect(res.headers.get('content-type')).toBe('text/markdown; charset=utf-8')
280
+ expect(await res.text()).toMatchInlineSnapshot(`
281
+ "# Services
282
+
283
+ ## [OpenAI](/discover/openai.md)
284
+
285
+ Chat completions, embeddings, image generation, and audio transcription.
286
+
287
+ ### Routes
288
+
289
+ - \`POST /v1/chat/completions\`: Chat completion
290
+ - Type: charge
291
+ - Price: 0.05 (50000 units, 6 decimals)
292
+ - Currency: 0x20c0000000000000000000000000000000000001
293
+ - Docs: https://context7.com/websites/platform_openai/llms.txt?topic=POST%20%2Fv1%2Fchat%2Fcompletions
294
+
295
+ - \`GET /v1/models\`
296
+ - Type: free
297
+ - Docs: https://context7.com/websites/platform_openai/llms.txt?topic=GET%20%2Fv1%2Fmodels
298
+
299
+ ## [Anthropic](/discover/anthropic.md)
300
+
301
+ Claude language models for messages and completions.
302
+
303
+ ### Routes
304
+
305
+ - \`POST /v1/messages\`: Send message
306
+ - Type: charge
307
+ - Price: 0.03 (30000 units, 6 decimals)
308
+ - Currency: 0x20c0000000000000000000000000000000000001
309
+ "
310
+ `)
311
+ })
312
+
313
+ test('behavior: GET /discover/:id.md returns markdown', async () => {
314
+ const proxy = ApiProxy.create({
315
+ services: [
316
+ openai({
317
+ apiKey: 'sk-test',
318
+ routes: {
319
+ 'POST /v1/chat/completions': mppx_server.charge({
320
+ amount: '0.05',
321
+ description: 'Chat completion',
322
+ }),
323
+ 'GET /v1/models': true,
324
+ },
325
+ }),
326
+ anthropic({
327
+ apiKey: 'sk-ant-test',
328
+ routes: {
329
+ 'POST /v1/messages': mppx_server.charge({ amount: '0.03' }),
330
+ },
331
+ }),
332
+ ],
333
+ })
334
+ proxyServer = await Http.createServer(proxy.listener)
335
+
336
+ const res = await fetch(`${proxyServer.url}/discover/openai.md`)
337
+ expect(res.status).toBe(200)
338
+ expect(res.headers.get('content-type')).toBe('text/markdown; charset=utf-8')
339
+ expect(await res.text()).toMatchInlineSnapshot(`
340
+ "# OpenAI
341
+
342
+ > Documentation: https://context7.com/websites/platform_openai/llms.txt
343
+
344
+ Chat completions, embeddings, image generation, and audio transcription.
345
+
346
+ ## Routes
347
+
348
+ - \`POST /v1/chat/completions\`: Chat completion
349
+ - Type: charge
350
+ - Price: 0.05 (50000 units, 6 decimals)
351
+ - Currency: 0x20c0000000000000000000000000000000000001
352
+ - Docs: https://context7.com/websites/platform_openai/llms.txt?topic=POST%20%2Fv1%2Fchat%2Fcompletions
353
+
354
+ - \`GET /v1/models\`
355
+ - Type: free
356
+ - Docs: https://context7.com/websites/platform_openai/llms.txt?topic=GET%20%2Fv1%2Fmodels
357
+ "
358
+ `)
359
+ })
360
+
361
+ test('behavior: GET /discover/:id with Accept: text/markdown returns markdown', async () => {
362
+ const proxy = ApiProxy.create({
363
+ services: [
364
+ openai({
365
+ apiKey: 'sk-test',
366
+ routes: { 'GET /v1/models': true },
367
+ }),
368
+ anthropic({
369
+ apiKey: 'sk-ant-test',
370
+ routes: {
371
+ 'POST /v1/messages': mppx_server.charge({ amount: '0.03' }),
372
+ },
373
+ }),
374
+ ],
375
+ })
376
+ proxyServer = await Http.createServer(proxy.listener)
377
+
378
+ const res = await fetch(`${proxyServer.url}/discover/anthropic`, {
379
+ headers: { Accept: 'text/markdown' },
380
+ })
381
+ expect(res.status).toBe(200)
382
+ expect(res.headers.get('content-type')).toBe('text/markdown; charset=utf-8')
383
+ })
384
+
385
+ test('behavior: GET /discover/:id without Accept returns JSON', async () => {
386
+ const proxy = ApiProxy.create({
387
+ services: [
388
+ openai({
389
+ apiKey: 'sk-test',
390
+ routes: { 'GET /v1/models': true },
391
+ }),
392
+ anthropic({
393
+ apiKey: 'sk-ant-test',
394
+ routes: {
395
+ 'POST /v1/messages': mppx_server.charge({ amount: '0.03' }),
396
+ },
397
+ }),
398
+ ],
399
+ })
400
+ proxyServer = await Http.createServer(proxy.listener)
401
+
402
+ const res = await fetch(`${proxyServer.url}/discover/openai`)
403
+ expect(res.status).toBe(200)
404
+ expect(res.headers.get('content-type')).toMatchInlineSnapshot(`"application/json"`)
405
+ })
406
+
407
+ test('behavior: GET /discover/:id.md returns 404 for unknown', async () => {
408
+ const proxy = ApiProxy.create({ services: [] })
409
+ proxyServer = await Http.createServer(proxy.listener)
410
+ const res = await fetch(`${proxyServer.url}/discover/unknown.md`)
411
+ expect(res.status).toBe(404)
412
+ })
413
+
414
+ test('behavior: GET /discover/:id returns 404 for unknown', async () => {
240
415
  const proxy = ApiProxy.create({ services: [] })
241
416
  proxyServer = await Http.createServer(proxy.listener)
242
- const res = await fetch(`${proxyServer.url}/services/unknown`)
417
+ const res = await fetch(`${proxyServer.url}/discover/unknown`)
243
418
  expect(res.status).toBe(404)
244
419
  })
245
420
 
@@ -61,18 +61,63 @@ export function create(config: create.Config): Proxy {
61
61
  if (!pathname) return new Response('Not Found', { status: 404 })
62
62
 
63
63
  if (request.method === 'GET' && pathname === '/llms.txt')
64
- return new Response(Service.toLlmsTxt(config.services), {
65
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
66
- })
64
+ return new Response(
65
+ Service.toLlmsTxt(config.services, {
66
+ title: config.title,
67
+ description: config.description,
68
+ }),
69
+ { headers: { 'Content-Type': 'text/plain; charset=utf-8' } },
70
+ )
71
+
72
+ if (request.method === 'GET' && pathname === '/discover.md')
73
+ return new Response(
74
+ Service.toLlmsTxt(config.services, {
75
+ title: config.title,
76
+ description: config.description,
77
+ }),
78
+ { headers: { 'Content-Type': 'text/plain; charset=utf-8' } },
79
+ )
80
+
81
+ if (request.method === 'GET' && (pathname === '/discover' || pathname === '/discover/')) {
82
+ if (wantsMarkdown(request))
83
+ return new Response(
84
+ Service.toLlmsTxt(config.services, {
85
+ title: config.title,
86
+ description: config.description,
87
+ }),
88
+ { headers: { 'Content-Type': 'text/plain; charset=utf-8' } },
89
+ )
90
+ return Response.json(config.services.map(Service.serialize))
91
+ }
67
92
 
68
- if (request.method === 'GET' && (pathname === '/services' || pathname === '/services/'))
93
+ if (
94
+ request.method === 'GET' &&
95
+ (pathname === '/discover/all' || pathname === '/discover/all/')
96
+ ) {
97
+ if (wantsMarkdown(request))
98
+ return new Response(Service.toServicesMarkdown(config.services), {
99
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
100
+ })
69
101
  return Response.json(config.services.map(Service.serialize))
102
+ }
103
+
104
+ if (request.method === 'GET' && pathname === '/discover/all.md')
105
+ return new Response(Service.toServicesMarkdown(config.services), {
106
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
107
+ })
70
108
 
71
109
  {
72
- const match = pathname.match(/^\/services\/([^/]+)\/?$/)
110
+ // List service
111
+ const match =
112
+ pathname.match(/^\/discover\/([^/]+)\.md$/) ?? pathname.match(/^\/discover\/([^/]+)\/?$/)
73
113
  if (request.method === 'GET' && match) {
74
114
  const service = config.services.find((s) => s.id === match[1])
75
115
  if (!service) return new Response('Not Found', { status: 404 })
116
+ const wantsText = pathname.endsWith('.md') || wantsMarkdown(request)
117
+ if (wantsText)
118
+ return new Response(Service.toMarkdown(service), {
119
+ headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
120
+ })
76
121
  return Response.json(Service.serialize(service))
77
122
  }
78
123
  }
@@ -125,10 +170,14 @@ export declare namespace create {
125
170
  export type Config = {
126
171
  /** Base path prefix to strip before routing (e.g. `'/api/proxy'`). */
127
172
  basePath?: string | undefined
173
+ /** Short description of the proxy shown in `llms.txt`. */
174
+ description?: string | undefined
128
175
  /** Custom `fetch` implementation. Defaults to `globalThis.fetch`. */
129
176
  fetch?: typeof globalThis.fetch | undefined
130
177
  /** Services to proxy. Each service is mounted at `/{serviceId}/`. */
131
178
  services: Service.Service[]
179
+ /** Human-readable title for the proxy shown in `llms.txt`. */
180
+ title?: string | undefined
132
181
  }
133
182
  }
134
183
 
@@ -173,3 +222,42 @@ async function proxyUpstream(options: proxyUpstream.Options): Promise<Response>
173
222
 
174
223
  return upstreamRes
175
224
  }
225
+
226
+ const aiUserAgents = [
227
+ 'GPTBot',
228
+ 'OAI-SearchBot',
229
+ 'ChatGPT-User',
230
+ 'anthropic-ai',
231
+ 'ClaudeBot',
232
+ 'claude-web',
233
+ 'PerplexityBot',
234
+ 'Perplexity-User',
235
+ 'Google-Extended',
236
+ 'Googlebot',
237
+ 'Bingbot',
238
+ 'Amazonbot',
239
+ 'Applebot',
240
+ 'Applebot-Extended',
241
+ 'FacebookBot',
242
+ 'meta-externalagent',
243
+ 'Bytespider',
244
+ 'DuckAssistBot',
245
+ 'cohere-ai',
246
+ 'AI2Bot',
247
+ 'CCBot',
248
+ 'Diffbot',
249
+ 'YouBot',
250
+ 'MistralAI-User',
251
+ 'GoogleAgent-Mariner',
252
+ ]
253
+
254
+ const terminalUserAgents = ['curl', 'Wget', 'HTTPie', 'httpie-go', 'mppx', 'presto', 'xh']
255
+
256
+ function wantsMarkdown(request: globalThis.Request): boolean {
257
+ const accept = request.headers.get('accept')
258
+ if (accept && (accept.includes('text/markdown') || accept.includes('text/plain'))) return true
259
+ const ua = request.headers.get('user-agent') ?? ''
260
+ if (aiUserAgents.some((agent) => ua.includes(agent))) return true
261
+ if (terminalUserAgents.some((agent) => ua.includes(agent))) return true
262
+ return false
263
+ }
@@ -2,16 +2,22 @@ import { Value } from 'ox'
2
2
 
3
3
  /** A proxied upstream service with route definitions and optional request/response hooks. */
4
4
  export type Service = {
5
- /** Unique identifier used as the URL prefix (e.g. `'openai'` → `/{id}/...`). */
6
- id: string
7
5
  /** Base URL of the upstream service (e.g. `'https://api.openai.com'`). */
8
6
  baseUrl: string
9
- /** Map of route patterns to endpoint handlers. */
10
- routes: EndpointMap
7
+ /** Short description of the service. */
8
+ description?: string | undefined
9
+ /** Unique identifier used as the URL prefix (e.g. `'openai'` → `/{id}/...`). */
10
+ id: string
11
+ /** Returns a documentation URL. Called with no argument for the service root, or with a route pattern for per-endpoint docs. */
12
+ docsLlmsUrl?: ((options: { route?: string | undefined }) => string | undefined) | undefined
11
13
  /** Hook to modify the upstream request before sending (e.g. inject auth headers). */
12
14
  rewriteRequest?: ((req: Request, ctx: Context) => Request | Promise<Request>) | undefined
13
15
  /** Hook to modify the upstream response before returning to the client. */
14
16
  rewriteResponse?: ((res: Response, ctx: Context) => Response | Promise<Response>) | undefined
17
+ /** Map of route patterns to endpoint handlers. */
18
+ routes: EndpointMap
19
+ /** Human-readable title for the service (e.g. `'OpenAI'`). */
20
+ title?: string | undefined
15
21
  }
16
22
 
17
23
  /**
@@ -73,9 +79,12 @@ export type From<
73
79
  export function from<options = unknown>(id: string, config: from.Config<options>): Service {
74
80
  const rewriteFromConfig = resolveRewriteRequest(config)
75
81
  return {
76
- id,
77
82
  baseUrl: config.baseUrl,
83
+ description: config.description,
84
+ id,
85
+ docsLlmsUrl: resolveLlmsUrl(config.docsLlmsUrl),
78
86
  routes: config.routes,
87
+ title: config.title,
79
88
  rewriteRequest: config.rewriteRequest
80
89
  ? rewriteFromConfig
81
90
  ? async (req, ctx) => {
@@ -93,8 +102,15 @@ export declare namespace from {
93
102
  baseUrl: string
94
103
  /** Shorthand: inject `Authorization: Bearer {token}` header. */
95
104
  bearer?: string | undefined
105
+ /** Short description of the service. */
106
+ description?: string | undefined
96
107
  /** Shorthand: inject custom headers. */
97
108
  headers?: Record<string, string> | undefined
109
+ /** Documentation URL for the service. String for a static base URL, or a function receiving an optional endpoint pattern. */
110
+ docsLlmsUrl?:
111
+ | string
112
+ | ((options: { route?: string | undefined }) => string | undefined)
113
+ | undefined
98
114
  /** Shorthand: full request mutation function. Takes priority over `bearer`/`headers`. */
99
115
  mutate?: ((req: Request) => Request | Promise<Request>) | undefined
100
116
  /** Hook to modify the upstream request. Receives typed per-endpoint options via `ctx`. */
@@ -103,6 +119,8 @@ export declare namespace from {
103
119
  | undefined
104
120
  /** Map of route patterns to endpoint definitions. */
105
121
  routes: EndpointMap
122
+ /** Human-readable title for the service. */
123
+ title?: string | undefined
106
124
  }
107
125
  }
108
126
 
@@ -142,29 +160,34 @@ function resolveRewriteRequest(
142
160
  /** Serializes a service for discovery responses. */
143
161
  export function serialize(s: Service) {
144
162
  return {
145
- id: s.id,
146
163
  baseUrl: s.baseUrl,
164
+ description: s.description,
165
+ id: s.id,
166
+ docsLlmsUrl: s.docsLlmsUrl?.({}),
147
167
  routes: Object.entries(s.routes).map(([pattern, endpoint]) => {
148
168
  const tokens = pattern.trim().split(/\s+/)
149
169
  const hasMethod = tokens.length >= 2
150
170
  return {
171
+ docsLlmsUrl: s.docsLlmsUrl?.({ route: pattern }),
151
172
  method: hasMethod ? tokens[0] : undefined,
152
173
  path: hasMethod ? tokens.slice(1).join(' ') : tokens[0],
153
174
  pattern,
154
175
  payment: endpoint ? resolvePayment(endpoint) : null,
155
176
  }
156
177
  }),
178
+ title: s.title,
157
179
  }
158
180
  }
159
181
 
160
182
  /** Renders an llms.txt markdown string for a list of services. */
161
- export function toLlmsTxt(services: Service[]): string {
183
+ export function toLlmsTxt(
184
+ services: Service[],
185
+ options?: { title?: string | undefined; description?: string | undefined },
186
+ ): string {
162
187
  const lines: string[] = [
163
- '# API Proxy',
188
+ `# ${options?.title ?? 'API Proxy'}`,
164
189
  '',
165
- '> Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).',
166
- '',
167
- 'For machine-readable service data, use `GET /services` (JSON).',
190
+ `> ${options?.description ?? 'Paid API proxy powered by [Machine Payments Protocol](https://mpp.tempo.xyz).'}`,
168
191
  '',
169
192
  ]
170
193
 
@@ -172,38 +195,65 @@ export function toLlmsTxt(services: Service[]): string {
172
195
 
173
196
  lines.push('## Services', '')
174
197
  for (const s of services) {
175
- const serialized = serialize(s)
176
- const free = serialized.routes.filter((r) => r.payment === null).length
177
- const paid = serialized.routes.length - free
178
- const parts = [paid && `${paid} paid`, free && `${free} free`].filter(Boolean).join(', ')
179
- lines.push(`- [${s.id}](${s.baseUrl}): ${parts}`)
198
+ const label = s.title ?? s.id
199
+ const desc = s.description ? `: ${s.description}` : ''
200
+ lines.push(`- [${label}](/discover/${s.id}.md)${desc}`)
180
201
  }
202
+ lines.push('', '[See all service definitions](/discover/all.md)')
203
+
204
+ return lines.join('\n')
205
+ }
206
+
207
+ /** Renders a full markdown listing of all services with their routes. */
208
+ export function toServicesMarkdown(services: Service[]): string {
209
+ const lines: string[] = ['# Services', '']
210
+
211
+ if (services.length === 0) return lines.join('\n')
181
212
 
182
213
  for (const s of services) {
183
- const serialized = serialize(s)
184
- lines.push('', `## ${s.id}`, '')
185
- for (const route of serialized.routes) {
186
- if (!route.payment) {
187
- lines.push(`- \`${route.pattern}\`: Free`)
188
- continue
189
- }
190
- const p = route.payment as Record<string, unknown>
191
- const parts = [`${p.intent}`]
214
+ lines.push(`## [${s.title ?? s.id}](/discover/${s.id}.md)`, '')
215
+ if (s.description) lines.push(s.description, '')
216
+ pushRoutes(lines, s)
217
+ }
218
+
219
+ return lines.join('\n')
220
+ }
221
+
222
+ /** Renders a markdown string for a single service. */
223
+ export function toMarkdown(s: Service): string {
224
+ const docsLlmsUrl = s.docsLlmsUrl?.({})
225
+ const lines: string[] = [`# ${s.title ?? s.id}`, '']
226
+ if (docsLlmsUrl) lines.push(`> Documentation: ${docsLlmsUrl}`, '')
227
+ if (s.description) lines.push(s.description, '')
228
+ pushRoutes(lines, s, '##')
229
+ return lines.join('\n')
230
+ }
231
+
232
+ function pushRoutes(lines: string[], s: Service, heading: '##' | '###' = '###') {
233
+ lines.push(`${heading} Routes`, '')
234
+ const serialized = serialize(s)
235
+ for (const route of serialized.routes) {
236
+ const p = route.payment as Record<string, unknown> | null
237
+ const desc = p?.description ? `: ${p.description}` : ''
238
+ lines.push(`- \`${route.pattern}\`${desc}`)
239
+ if (!p) {
240
+ lines.push(' - Type: free')
241
+ } else {
242
+ lines.push(` - Type: ${p.intent}`)
192
243
  if (p.amount) {
193
- const unit = `${p.amount} units`
194
- parts.push(p.unitType ? `${unit} per ${p.unitType}` : unit)
244
+ const perUnit = p.unitType ? `/${p.unitType}` : ''
245
+ if (p.decimals !== undefined) {
246
+ const price = Number(p.amount) / 10 ** Number(p.decimals)
247
+ lines.push(` - Price: ${price}${perUnit} (${p.amount} units, ${p.decimals} decimals)`)
248
+ } else {
249
+ lines.push(` - Units: ${p.amount}${perUnit}`)
250
+ }
195
251
  }
196
- if (p.description) parts.push(`"${p.description}"`)
197
- const meta = [
198
- p.currency && `currency: ${p.currency}`,
199
- p.decimals !== undefined && `decimals: ${p.decimals}`,
200
- ].filter(Boolean)
201
- if (meta.length) parts.push(`(${meta.join(', ')})`)
202
- lines.push(`- \`${route.pattern}\`: ${parts.join(' — ')}`)
252
+ if (p.currency) lines.push(` - Currency: ${p.currency}`)
203
253
  }
254
+ if (route.docsLlmsUrl) lines.push(` - Docs: ${route.docsLlmsUrl}`)
255
+ lines.push('')
204
256
  }
205
-
206
- return lines.join('\n')
207
257
  }
208
258
 
209
259
  /** Extracts per-endpoint options from an endpoint definition. */
@@ -225,3 +275,11 @@ function resolvePayment(endpoint: Endpoint): Record<string, unknown> | null {
225
275
  })()
226
276
  return { intent, method: name, ...rest, ...(amount !== undefined && { amount }) }
227
277
  }
278
+
279
+ function resolveLlmsUrl(
280
+ input: string | ((options: { route?: string | undefined }) => string | undefined) | undefined,
281
+ ): Service['docsLlmsUrl'] {
282
+ if (!input) return undefined
283
+ if (typeof input === 'function') return input
284
+ return ({ route }) => (route ? undefined : input)
285
+ }
@@ -20,12 +20,14 @@ import * as Service from '../Service.js'
20
20
  export function anthropic(config: anthropic.Config) {
21
21
  return Service.from<anthropic.Config>('anthropic', {
22
22
  baseUrl: config.baseUrl ?? 'https://api.anthropic.com',
23
+ description: 'Claude language models for messages and completions.',
23
24
  rewriteRequest(request, ctx) {
24
25
  const apiKey = ctx.apiKey ?? config.apiKey
25
26
  request.headers.set('x-api-key', apiKey)
26
27
  return request
27
28
  },
28
29
  routes: config.routes,
30
+ title: 'Anthropic',
29
31
  })
30
32
  }
31
33
 
@@ -20,12 +20,18 @@ import * as Service from '../Service.js'
20
20
  export function openai(config: openai.Config) {
21
21
  return Service.from<openai.Config>('openai', {
22
22
  baseUrl: config.baseUrl ?? 'https://api.openai.com',
23
+ description: 'Chat completions, embeddings, image generation, and audio transcription.',
24
+ docsLlmsUrl: ({ route }) =>
25
+ route
26
+ ? `https://context7.com/websites/platform_openai/llms.txt?topic=${encodeURIComponent(route)}`
27
+ : 'https://context7.com/websites/platform_openai/llms.txt',
23
28
  rewriteRequest(request, ctx) {
24
29
  const apiKey = ctx.apiKey ?? config.apiKey
25
30
  request.headers.set('Authorization', `Bearer ${apiKey}`)
26
31
  return request
27
32
  },
28
33
  routes: config.routes,
34
+ title: 'OpenAI',
29
35
  })
30
36
  }
31
37
 
@@ -35,6 +41,7 @@ export declare namespace openai {
35
41
  apiKey: string
36
42
  /** Base URL override. Defaults to `'https://api.openai.com'`. */
37
43
  baseUrl?: string | undefined
44
+ /** Route definitions for OpenAI endpoints. */
38
45
  routes:
39
46
  | 'POST /v1/chat/completions'
40
47
  | 'POST /v1/completions'
@@ -20,12 +20,18 @@ import * as Service from '../Service.js'
20
20
  export function stripe(config: stripe.Config) {
21
21
  return Service.from<stripe.Config>('stripe', {
22
22
  baseUrl: config.baseUrl ?? 'https://api.stripe.com',
23
+ description: 'Payment processing, customers, subscriptions, and invoices.',
24
+ docsLlmsUrl: ({ route }) =>
25
+ route
26
+ ? `https://context7.com/websites/stripe/llms.txt?topic=${encodeURIComponent(route)}`
27
+ : 'https://docs.stripe.com/llms.txt',
23
28
  rewriteRequest(request, ctx) {
24
29
  const apiKey = ctx.apiKey ?? config.apiKey
25
30
  request.headers.set('Authorization', `Basic ${btoa(`${apiKey}:`)}`)
26
31
  return request
27
32
  },
28
33
  routes: config.routes,
34
+ title: 'Stripe',
29
35
  })
30
36
  }
31
37