1- import { Operation , OperationResult } from './api-types' ;
2-
3- const PRO_REQUIRED_ERROR : OperationResult = {
4- success : false ,
5- error : {
6- code : 'PRO_REQUIRED' ,
7- message : 'This feature requires an HTTP Toolkit Pro subscription. ' +
8- 'Get Pro at https://httptoolkit.com/pricing/ to unlock ' +
9- 'programmatic access to HTTP Toolkit via MCP, CLI, and more.'
10- }
11- } ;
1+ import { Operation , OperationDefinition , OperationResult } from './api-types' ;
2+
3+ function tierRequiredError ( tier : string ) : OperationResult {
4+ const displayTier = tier . charAt ( 0 ) . toUpperCase ( ) + tier . slice ( 1 ) ;
5+ return {
6+ success : false ,
7+ error : {
8+ code : `TIER_REQUIRED_${ tier . toUpperCase ( ) } ` ,
9+ message : `This feature requires an HTTP Toolkit ${ displayTier } subscription. ` +
10+ `Get ${ displayTier } at https://httptoolkit.com/pricing/ to unlock ` +
11+ 'programmatic access to HTTP Toolkit via MCP, CLI, and more.'
12+ }
13+ } ;
14+ }
1215
1316export class OperationRegistry {
1417
1518 private operations = new Map < string , Operation > ( ) ;
19+ private sessionLimitCounters = new Map < string , number > ( ) ;
1620
1721 constructor ( private isPaidUser : ( ) => boolean ) { }
1822
1923 register ( op : Operation ) : void {
2024 this . operations . set ( op . definition . name , op ) ;
2125 }
2226
23- getDefinitions ( ) {
24- return Array . from ( this . operations . values ( ) ) . map ( op => op . definition ) ;
27+ private getUserTier ( ) : 'free' | 'pro' {
28+ return this . isPaidUser ( ) ? 'pro' : 'free' ;
2529 }
2630
27- async execute ( name : string , params : Record < string , unknown > ) : Promise < OperationResult > {
28- if ( ! this . isPaidUser ( ) ) {
29- return PRO_REQUIRED_ERROR ;
30- }
31+ getDefinitions ( ) : OperationDefinition [ ] {
32+ const tier = this . getUserTier ( ) ;
33+
34+ return Array . from ( this . operations . values ( ) )
35+ . filter ( op => {
36+ const { tiers } = op . definition ;
37+ // Hide operations whose tiers don't include the current user's tier
38+ // (e.g. account.upgrade has tiers: ['free'] — hidden from pro users)
39+ if ( ! tiers . includes ( tier ) ) return false ;
40+ return true ;
41+ } )
42+ . map ( op => {
43+ if ( tier === 'pro' ) return op . definition ;
44+
45+ // Augment descriptions for free users so AI agents understand limits
46+ let description = op . definition . description ;
47+ const { tiers, sessionLimit } = op . definition ;
48+
49+ if ( sessionLimit ) {
50+ description += `\n\n[Free tier] Limited to ${ sessionLimit } calls per session. ` +
51+ 'Use account.upgrade to subscribe to Pro for unlimited access.' ;
52+ }
53+
54+ if ( description === op . definition . description ) return op . definition ;
55+ return { ...op . definition , description } ;
56+ } ) ;
57+ }
3158
59+ async execute ( name : string , params : Record < string , unknown > ) : Promise < OperationResult > {
3260 const op = this . operations . get ( name ) ;
3361 if ( ! op ) {
3462 return {
@@ -40,6 +68,30 @@ export class OperationRegistry {
4068 } ;
4169 }
4270
71+ const tier = this . getUserTier ( ) ;
72+ const { tiers, sessionLimit } = op . definition ;
73+
74+ if ( ! tiers . includes ( tier ) ) {
75+ const requiredTier = tiers [ 0 ] ; // The first listed tier the user doesn't have
76+ return tierRequiredError ( requiredTier ) ;
77+ }
78+
79+ // Check session limits for free users
80+ if ( tier === 'free' && sessionLimit ) {
81+ const count = this . sessionLimitCounters . get ( name ) ?? 0 ;
82+ if ( count >= sessionLimit ) {
83+ return {
84+ success : false ,
85+ error : {
86+ code : 'SESSION_LIMIT' ,
87+ message : `Free users are limited to ${ sessionLimit } calls per session. ` +
88+ 'Upgrade to HTTP Toolkit Pro for unlimited access: https://httptoolkit.com/pricing/'
89+ }
90+ } ;
91+ }
92+ this . sessionLimitCounters . set ( name , count + 1 ) ;
93+ }
94+
4395 try {
4496 const result = await op . handler ( params ) ;
4597 // JSON roundtrip to strip MobX observables, class instances, and other
0 commit comments