trackly-cli 0.1.12 → 0.2.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.
package/README.md CHANGED
@@ -7,9 +7,23 @@
7
7
 
8
8
  The only job tracking CLI built for AI agents.
9
9
 
10
- Search 128,000+ jobs across 1,900+ companies and 40+ ATS types. Track applications, get AI-powered recommendations, and manage your job search -- from the terminal or through Claude Code, Cursor, and other MCP-compatible AI agents.
10
+ Search 128,000+ jobs across 1,900+ companies and 40+ ATS types. Track applications, get AI-powered recommendations, and manage your job search -- from the terminal or through Claude, ChatGPT, Cursor, and other MCP-compatible AI agents.
11
11
 
12
- ## Quick Start
12
+ ## Two ways to connect
13
+
14
+ ### 🚀 Option 1: One-click in Claude co-work, Claude Desktop, ChatGPT (no install)
15
+
16
+ Use Trackly directly inside your AI — zero config:
17
+
18
+ 1. Open Settings → Connectors → **Add custom connector**
19
+ 2. URL: `https://mcp.usetrackly.app/api/mcp`
20
+ 3. Click **Add** → sign in with Google → done
21
+
22
+ **[Full setup guide with screenshots →](https://usetrackly.app/connect)**
23
+
24
+ Works in: Claude co-work (web), Claude Desktop, ChatGPT Connectors, and any MCP client that supports remote/streamable-http connectors.
25
+
26
+ ### 💻 Option 2: CLI install (for Cursor, Windsurf, or terminal use)
13
27
 
14
28
  ```bash
15
29
  npm install -g trackly-cli # may need: sudo npm install -g trackly-cli
@@ -27,8 +41,10 @@ trackly jobs --function product
27
41
 
28
42
  ```bash
29
43
  trackly jobs # List jobs
30
- trackly jobs --modality remote # Filter remote jobs
31
- trackly jobs --function product # Filter by function
44
+ trackly jobs --remote # Filter remote jobs (sets usStates=REMOTE)
45
+ trackly jobs --region us # Filter by region (us, non_us, all, or a region tag: europe, canada, remote, ...)
46
+ trackly jobs --job-type internship # Filter by employment type (full_time, internship, all)
47
+ trackly jobs --function product # Filter by function
32
48
  trackly jobs --company 243 # Filter by company ID
33
49
  trackly job 1234 # Get job details
34
50
  trackly jobs 1234 # Alias for job details
@@ -60,7 +76,19 @@ Add `--json` to any command for JSON output. Use `--api-key <key>` or `--base-ur
60
76
 
61
77
  ## MCP Server Setup
62
78
 
63
- ### One-liner (recommended)
79
+ ### Hosted (Claude co-work, Claude Desktop, ChatGPT)
80
+
81
+ No install. In your AI tool, open **Settings → Connectors → Add custom connector** and enter:
82
+
83
+ ```text
84
+ https://mcp.usetrackly.app/api/mcp
85
+ ```
86
+
87
+ Sign in with Google when prompted. [Full visual guide →](https://usetrackly.app/connect)
88
+
89
+ ### Local (CLI via stdio, for Cursor / Windsurf / Claude Code)
90
+
91
+ #### Claude Code one-liner
64
92
 
65
93
  ```bash
66
94
  claude mcp add --scope user trackly -- trackly mcp
@@ -72,7 +100,7 @@ Or equivalently:
72
100
  claude mcp add-json --scope user trackly '{"command":"trackly","args":["mcp"]}'
73
101
  ```
74
102
 
75
- ### Claude Code manual config
103
+ #### Claude Code manual config
76
104
 
77
105
  Add to `~/.claude/settings.json`:
78
106
 
@@ -87,9 +115,9 @@ Add to `~/.claude/settings.json`:
87
115
  }
88
116
  ```
89
117
 
90
- ### Cursor
118
+ #### Cursor / Windsurf
91
119
 
92
- Add to `.cursor/mcp.json` or `~/.cursor/mcp.json`:
120
+ Add to `.cursor/mcp.json` or `~/.cursor/mcp.json` (same schema works for Windsurf):
93
121
 
94
122
  ```json
95
123
  {
@@ -102,7 +130,7 @@ Add to `.cursor/mcp.json` or `~/.cursor/mcp.json`:
102
130
  }
103
131
  ```
104
132
 
105
- Then use natural language in Claude Code or Cursor:
133
+ Then use natural language in any of these clients:
106
134
 
107
135
  - "Find me PM jobs at fintech companies"
108
136
  - "What remote engineering roles are available?"
package/bin/trackly CHANGED
@@ -19,17 +19,17 @@ const crypto = require('crypto');
19
19
  const { spawnSync } = require('child_process');
20
20
  const { version: VERSION } = require('../package.json');
21
21
 
22
- const BOOLEAN_FLAGS = new Set(['help', 'h', 'json', 'version', 'v', 'clear-api-key', 'clear-base-url', 'refresh']);
22
+ const BOOLEAN_FLAGS = new Set(['help', 'h', 'json', 'version', 'v', 'clear-api-key', 'clear-base-url', 'refresh', 'remote']);
23
23
  const VALUE_FLAGS = new Set([
24
24
  'api-key',
25
25
  'base-url',
26
26
  'function',
27
+ 'job-type',
27
28
  'keywords',
28
29
  'limit',
29
- 'location',
30
- 'modality',
31
30
  'name',
32
31
  'offset',
32
+ 'region',
33
33
  'search',
34
34
  'sort',
35
35
  'status',
@@ -128,9 +128,21 @@ function parseJobId(value) {
128
128
  return id;
129
129
  }
130
130
 
131
+ // Flags deprecated in the API-docs-plan fixup. Still accepted by parseArgs so old
132
+ // scripts don't ERR_NOT_FOUND, but cmdJobs hard-rejects them with a migration hint
133
+ // — silent no-op would be worse than noise (per PR #13 Codex P1 finding).
134
+ const DEPRECATED_JOB_FLAGS = Object.freeze({
135
+ location: '--location was removed. Use --region <tag> (us, non_us, all, or a region tag like europe, canada, remote). See `trackly --help` for accepted values.',
136
+ modality: '--modality was removed. Use --job-type <full_time|internship|all> for employment type, and/or --remote / --region remote for work-location.',
137
+ });
138
+
131
139
  function hasJobListFilters(args) {
132
- return ['function', 'keywords', 'limit', 'location', 'modality', 'offset', 'sort', 'status']
133
- .some((key) => args[key] !== undefined);
140
+ // Include deprecated flags so `trackly jobs 42 --location us` routes to cmdJobs
141
+ // (which then raises the migration error) instead of cmdJob(42) silently ignoring
142
+ // the filter. See PR #13 Cursor Bugbot finding.
143
+ const deprecated = Object.keys(DEPRECATED_JOB_FLAGS);
144
+ const active = ['function', 'company', 'keywords', 'limit', 'region', 'job-type', 'remote', 'offset', 'sort', 'status'];
145
+ return [...active, ...deprecated].some((key) => args[key] !== undefined);
134
146
  }
135
147
 
136
148
  function openExternalUrl(url) {
@@ -350,11 +362,25 @@ async function cmdWhoami() {
350
362
  async function cmdJobs(args) {
351
363
  if (!hasAuth()) failNow(AUTH_HELP_TEXT);
352
364
 
365
+ // Reject deprecated flags with migration hints instead of silently ignoring them
366
+ // (scripts from older releases would otherwise appear to succeed while returning
367
+ // an unfiltered feed — misleading and hard to debug).
368
+ for (const [flag, hint] of Object.entries(DEPRECATED_JOB_FLAGS)) {
369
+ if (args[flag] !== undefined) failNow(hint);
370
+ }
371
+
372
+ // --remote is a shorthand that conflicts with --region (they both control location).
373
+ // Help text documents them as alternatives; enforce it explicitly here.
374
+ if (args.remote && args.region) {
375
+ failNow('--remote cannot be combined with --region. Use one or the other (e.g. --region remote).');
376
+ }
377
+
353
378
  const params = new URLSearchParams();
354
379
  if (args.function) params.set('jobFunction', args.function);
355
380
  if (args.company) params.set('companyId', args.company);
356
- if (args.location) params.set('location', args.location);
357
- if (args.modality) params.set('modality', args.modality);
381
+ if (args.region) params.set('locationFilter', args.region);
382
+ if (args['job-type']) params.set('jobModality', args['job-type']);
383
+ if (args.remote) params.set('usStates', 'REMOTE');
358
384
  if (args.status) params.set('status', args.status);
359
385
  if (args.sort) params.set('sort', args.sort);
360
386
  if (args.limit) params.set('limit', args.limit);
@@ -384,16 +410,15 @@ async function cmdJob(args) {
384
410
 
385
411
  const job = result.job || result;
386
412
  console.log(color('bold', job.title));
387
- console.log(`Company: ${color('blue', job.company_name)}`);
413
+ console.log(`Company: ${color('blue', job.companyName || (job.company && job.company.name) || '')}`);
388
414
  if (job.location) console.log(`Location: ${job.location}`);
389
- if (job.modality) console.log(`Modality: ${color('cyan', job.modality)}`);
390
- if (job.url) console.log(`URL: ${color('dim', job.url)}`);
391
- if (job.posted_at) console.log(`Posted: ${job.posted_at}`);
392
- if (job.description_text) {
415
+ if (job.jobUrl) console.log(`URL: ${color('dim', job.jobUrl)}`);
416
+ if (job.postedAt) console.log(`Posted: ${job.postedAt}`);
417
+ if (typeof job.description === 'string' && job.description.length > 0) {
393
418
  console.log();
394
419
  console.log(color('bold', 'Description:'));
395
- console.log(job.description_text.slice(0, 1000));
396
- if (job.description_text.length > 1000) console.log(color('dim', '... (truncated)'));
420
+ console.log(job.description.slice(0, 1000));
421
+ if (job.description.length > 1000) console.log(color('dim', '... (truncated)'));
397
422
  }
398
423
  } catch (e) {
399
424
  setErrorExit(e.error || e.message || 'Failed to fetch job');
@@ -700,7 +725,7 @@ ${color('bold', 'Commands:')}
700
725
  ${color('cyan', 'login')} Authenticate via Google OAuth
701
726
  ${color('cyan', 'logout')} Clear credentials
702
727
  ${color('cyan', 'whoami')} Show current user
703
- ${color('cyan', 'jobs')} List jobs (--function, --company <id>, --location, --modality, --status, --sort, --limit, --offset, --keywords)
728
+ ${color('cyan', 'jobs')} List jobs (--function, --company <id>, --region <tag>, --job-type <type>, --remote, --status, --sort, --limit, --offset, --keywords)
704
729
  ${color('cyan', 'job <id>')} Get full job detail
705
730
  ${color('cyan', 'jobs <id>')} Alias for job <id>
706
731
  ${color('cyan', 'companies')} List companies
@@ -732,6 +757,18 @@ ${color('bold', 'Flags:')}
732
757
  --version Show version
733
758
  --help Show this help
734
759
 
760
+ ${color('bold', 'Filter values:')}
761
+ --function product, engineering, design, data, marketing, sales, partnerships,
762
+ finance, strategy, operations, people, legal, support, other (comma-separated)
763
+ --region us, non_us, all, or a region tag (europe, canada, remote, ...)
764
+ Use a comma-separated list of region tags for multiple regions;
765
+ us/non_us/all cannot be combined.
766
+ --job-type full_time, internship, all (employment type, not work-location)
767
+ --remote Flag (no value). Filters for remote jobs (equivalent to usStates=REMOTE).
768
+ --status new, applying, applied_confirmed, check_later, not_interested, all
769
+ (filters by YOUR application pipeline state)
770
+ --sort newest, oldest, company
771
+
735
772
  ${color('bold', 'MCP Setup:')}
736
773
  Claude Code: claude mcp add --scope user trackly -- trackly mcp
737
774
  Cursor: add {"command":"trackly","args":["mcp"]} to .cursor/mcp.json
@@ -1,12 +1,27 @@
1
1
  ## Trackly Job Tracker (MCP)
2
2
 
3
- Trackly MCP server provides job search and tracking tools. Start it with:
3
+ Trackly MCP server provides job search and tracking tools. Two ways to connect:
4
+
5
+ ### Claude co-work, Claude Desktop, ChatGPT (hosted, no install)
6
+
7
+ In your AI tool, open **Settings → Connectors → Add custom connector**:
8
+
9
+ - URL: `https://mcp.usetrackly.app/api/mcp`
10
+ - Leave any optional OAuth fields empty — **Client ID**, **Client Secret**, **Authorization URL**, **Token URL** — Trackly uses OAuth 2.1 Dynamic Client Registration and will register the client automatically
11
+ - If the client UI uses different labels or requires you to continue past an OAuth section, keep those fields blank and proceed to sign in. Do not invent client credentials or endpoint URLs.
12
+
13
+ Sign in with Google when prompted. [Visual guide →](https://usetrackly.app/connect)
14
+
15
+ ### Cursor, Windsurf, Claude Code (CLI via stdio)
16
+
17
+ Install and start the stdio server:
4
18
 
5
19
  ```bash
20
+ npm install -g trackly-cli
6
21
  trackly mcp
7
22
  ```
8
23
 
9
- ### Claude Code
24
+ #### Claude Code
10
25
 
11
26
  ```json
12
27
  {
@@ -19,9 +34,9 @@ trackly mcp
19
34
  }
20
35
  ```
21
36
 
22
- ### Cursor
37
+ #### Cursor / Windsurf
23
38
 
24
- Add to `.cursor/mcp.json`:
39
+ Add to `.cursor/mcp.json` (same schema for Windsurf):
25
40
 
26
41
  ```json
27
42
  {
@@ -52,7 +67,16 @@ You can also pass `TRACKLY_API_KEY` as an environment variable for one-off runs.
52
67
 
53
68
  ### Available Tools
54
69
 
55
- - **trackly_search_jobs** — Search/filter jobs by function, company, location, modality, keywords. Function values: `product`, `engineering`, `design`, `data`, `marketing`, `sales`, `finance`, `operations`, `legal`, `people`, `strategy`, `support`, `other`
70
+ - **trackly_search_jobs** — Search/filter jobs.
71
+ - `function` (14 values): `product`, `engineering`, `design`, `data`, `marketing`, `sales`, `partnerships`, `finance`, `strategy`, `operations`, `people`, `legal`, `support`, `other`
72
+ - `locationFilter`: pass ONE of
73
+ - a single scalar: `us`, `non_us`, `all`, or a region tag (`europe`, `latam`, `middle_east`, `asia`, `africa`, `canada`, `oceania`, `remote`, `unknown`), OR
74
+ - an array of region tags for multi-region (e.g. `["europe", "canada"]`). The array form excludes `us` — combining `us` with other tags in an array causes the backend to silently drop the others. To get "not US" use the scalar `non_us` alone.
75
+ - `jobModality`: `full_time`, `internship`, `all` — employment type, NOT work-location style. For remote/hybrid/onsite filtering, use the `remote` boolean or `locationFilter: "remote"`; hybrid and onsite are not exposed as filters.
76
+ - `remote` (boolean): filter to remote jobs only. Maps to `usStates=REMOTE`.
77
+ - `status`: your application pipeline state. Values: `new`, `applying`, `applied_confirmed`, `check_later`, `not_interested`, `all`.
78
+ - `sort`: `newest`, `oldest`, `company`.
79
+ - `keywords`, `companyId`, `limit`, `offset`.
56
80
  - **trackly_get_job** — Get full job details by ID
57
81
  - **trackly_search_companies** — Semantic company search
58
82
  - **trackly_list_companies** — List all tracked companies with job counts
package/lib/formatters.js CHANGED
@@ -39,14 +39,14 @@ function outputJobs(jobs) {
39
39
  return;
40
40
  }
41
41
  for (const job of jobs) {
42
- const modality = job.modality ? color('cyan', `[${job.modality}]`) : '';
43
42
  const location = job.location ? color('dim', job.location) : '';
44
- console.log(`${color('bold', job.title)} ${modality}`);
45
- console.log(` ${color('blue', job.company_name)} | ${location}`);
43
+ const companyName = job.companyName || (job.company && job.company.name) || '';
44
+ console.log(`${color('bold', job.title)}`);
45
+ console.log(` ${color('blue', companyName)} | ${location}`);
46
46
  const fundingLine = formatFundingLine(job.company);
47
47
  if (fundingLine) console.log(` ${color('magenta', fundingLine)}`);
48
- console.log(` ID: ${color('dim', job.id)} | Posted: ${color('dim', job.posted_at || job.first_seen_at || 'unknown')}`);
49
- if (job.url) console.log(` ${color('dim', job.url)}`);
48
+ console.log(` ID: ${color('dim', job.id)} | Posted: ${color('dim', job.postedAt || job.firstSeenAt || 'unknown')}`);
49
+ if (job.jobUrl) console.log(` ${color('dim', job.jobUrl)}`);
50
50
  console.log();
51
51
  }
52
52
  }
@@ -58,7 +58,7 @@ function outputCompanies(companies) {
58
58
  return;
59
59
  }
60
60
  for (const co of companies) {
61
- const count = co.active_jobs_count || co.job_count || 0;
61
+ const count = co.totalJobCount || 0;
62
62
  console.log(`${color('bold', co.name)} — ${color('cyan', count + ' jobs')}`);
63
63
  if (co.domain) console.log(` ${color('dim', co.domain)}`);
64
64
  console.log();
package/mcp/server.js CHANGED
@@ -9,6 +9,33 @@ const { version: PACKAGE_VERSION } = require('../package.json');
9
9
  const MCP_USER_AGENT = `trackly-mcp/${PACKAGE_VERSION}`;
10
10
  const AUTH_HINT = 'Run `trackly login` or set TRACKLY_API_KEY. Get a key at https://usetrackly.app (sign in → Settings → API Keys).';
11
11
 
12
+ // Mirrors `granola-followup-app/src/services/region-classifier.ts:8` REGION_TAGS.
13
+ // Keep in sync when the backend enum changes.
14
+ const REGION_TAGS = [
15
+ 'us', 'europe', 'latam', 'middle_east', 'asia', 'africa', 'canada', 'oceania', 'remote', 'unknown',
16
+ ];
17
+
18
+ // REGION_TAGS values that are safe to combine in a comma-list with other tags.
19
+ // `us` is excluded because combining it with other tags (e.g. ['us', 'europe']) is a trap:
20
+ // the backend parser at granola-followup-app/src/routes/jobscout-filter-utils.ts:73-90
21
+ // supports `us` ONLY as a single-value scalar; in a comma-list it behaves identically to
22
+ // the scalar branch and any non-us members are ignored. Callers who want 'us + europe' should
23
+ // use the scalar `all` or two separate calls. Callers who want 'not us' should use `non_us`.
24
+ const REGION_TAGS_ARRAY_SAFE = REGION_TAGS.filter((t) => t !== 'us');
25
+
26
+ // `jobFunction` enum matches `granola-followup-app/src/routes/jobscout-filter-utils.ts:17-21`
27
+ // (ALL_JOB_FUNCTIONS). 14 canonical values.
28
+ const JOB_FUNCTIONS = [
29
+ 'product', 'engineering', 'design', 'data', 'marketing', 'sales', 'partnerships',
30
+ 'finance', 'strategy', 'operations', 'people', 'legal', 'support', 'other',
31
+ ];
32
+
33
+ // `status` enum matches the backend allowlist at `jobscout.ts:2949`.
34
+ const STATUS_VALUES = ['new', 'applying', 'applied_confirmed', 'check_later', 'not_interested', 'all'];
35
+
36
+ // `jobModality` enum matches `jobscout.ts:2870-2875`. Employment type, NOT work-location.
37
+ const JOB_MODALITIES = ['full_time', 'internship', 'all'];
38
+
12
39
  function createErrorResult(error, fallbackMessage, extra = {}) {
13
40
  const payload = {
14
41
  error: error?.error || error?.message || fallbackMessage,
@@ -66,31 +93,46 @@ function createServer() {
66
93
 
67
94
  server.tool(
68
95
  'trackly_search_jobs',
69
- 'Search and filter job postings. Returns matching jobs with title, company, location, modality. Use companyId to filter jobs at a specific company (get companyId from trackly_search_companies first).',
96
+ 'Search and filter job postings. Returns matching jobs with title, company, location, and structured fields. Use companyId to filter jobs at a specific company (get companyId from trackly_search_companies first). Pass `remote: true` for remote-only jobs.',
70
97
  {
71
- function: z.enum(['product', 'engineering', 'design', 'data', 'marketing', 'sales', 'finance', 'operations', 'legal', 'people', 'strategy', 'support', 'other']).optional().describe('Job function filter (matches DB column). Common: product, engineering, design, data, marketing, sales, operations, other'),
98
+ function: z.enum(JOB_FUNCTIONS).optional().describe('Job function filter. One of: ' + JOB_FUNCTIONS.join(', ')),
72
99
  companyId: z.number().optional().describe('Filter jobs by company ID (get from trackly_search_companies)'),
73
- location: z.string().optional().describe('Location filter (city or state)'),
74
- modality: z.enum(['remote', 'hybrid', 'onsite']).optional().describe('Work modality: remote, hybrid, onsite'),
75
- status: z.enum(['new', 'saved', 'applied', 'dismissed']).optional().describe('Application status: new, saved, applied, dismissed'),
100
+ locationFilter: z.union([
101
+ z.enum(['us', 'non_us', 'all']),
102
+ z.enum(REGION_TAGS),
103
+ z.array(z.enum(REGION_TAGS_ARRAY_SAFE)).min(1),
104
+ ]).optional().describe(
105
+ "Region tag filter. Pass ONE of: (a) a single scalar from 'us', 'non_us', 'all', or a REGION_TAGS value ('europe', 'latam', 'middle_east', 'asia', 'africa', 'canada', 'oceania', 'remote', 'unknown'); or (b) an array of region tags for multi-region (e.g. ['europe', 'canada']). The array form excludes 'us' — combining 'us' with other tags causes the backend to silently drop the others. For 'not US' use the scalar 'non_us' alone."
106
+ ),
107
+ jobModality: z.enum(JOB_MODALITIES).optional().describe(
108
+ "Employment type (NOT work-location style). full_time = full-time roles, internship = internships, all = both. For remote filtering, use the `remote` boolean or locationFilter='remote'. Hybrid and onsite are not exposed as filters."
109
+ ),
110
+ remote: z.boolean().optional().describe('Filter to remote jobs only (maps to usStates=REMOTE).'),
111
+ status: z.enum(STATUS_VALUES).optional().describe(
112
+ 'Filter by YOUR application pipeline state. Not a generic job-posting status. Values: ' + STATUS_VALUES.join(', ')
113
+ ),
76
114
  sort: z.enum(['newest', 'oldest', 'company']).optional().describe('Sort order: newest, oldest, company'),
77
115
  limit: z.number().max(50).optional().describe('Max results (default 20, max 50)'),
78
116
  offset: z.number().min(0).optional().describe('Pagination offset'),
79
- keywords: z.string().max(500).optional().describe('Keyword search in title/description'),
117
+ keywords: z.string().max(500).optional().describe('Keyword search in title, company, or description'),
80
118
  },
81
119
  wrapTool(async (params) => {
82
- // Map MCP param names to backend query param names
83
- const paramMap = {
84
- function: 'jobFunction',
85
- keywords: 'search',
86
- };
87
120
  const qs = new URLSearchParams();
88
- for (const [key, value] of Object.entries(params)) {
89
- if (value !== undefined && value !== null) {
90
- const backendKey = paramMap[key] || key;
91
- qs.set(backendKey, String(value));
92
- }
121
+ if (params.function !== undefined) qs.set('jobFunction', params.function);
122
+ if (params.companyId !== undefined) qs.set('companyId', String(params.companyId));
123
+ if (params.locationFilter !== undefined) {
124
+ const value = Array.isArray(params.locationFilter)
125
+ ? params.locationFilter.join(',')
126
+ : params.locationFilter;
127
+ qs.set('locationFilter', value);
93
128
  }
129
+ if (params.jobModality !== undefined) qs.set('jobModality', params.jobModality);
130
+ if (params.remote === true) qs.set('usStates', 'REMOTE');
131
+ if (params.status !== undefined) qs.set('status', params.status);
132
+ if (params.sort !== undefined) qs.set('sort', params.sort);
133
+ if (params.limit !== undefined) qs.set('limit', String(params.limit));
134
+ if (params.offset !== undefined) qs.set('offset', String(params.offset));
135
+ if (params.keywords !== undefined) qs.set('search', params.keywords);
94
136
  return apiRequest('GET', `/api/jobscout/jobs?${qs.toString()}`, null, false, false, MCP_USER_AGENT);
95
137
  }, 'Failed to search jobs')
96
138
  );
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "trackly-cli",
3
- "version": "0.1.12",
3
+ "version": "0.2.1",
4
4
  "mcpName": "io.github.kevinastuhuaman/trackly",
5
- "description": "CLI + MCP server for job tracking — search, apply, and manage applications from terminal or AI agents",
5
+ "description": "AI job search CLI + hosted MCP server with OAuth. 128K+ jobs, 1,900+ companies, 40+ ATS. Works with Claude, ChatGPT, Cursor, Windsurf, Codex via hosted streamable-http or local stdio.",
6
6
  "bin": {
7
7
  "trackly": "./bin/trackly"
8
8
  },
@@ -25,8 +25,16 @@
25
25
  "mcp",
26
26
  "mcp-server",
27
27
  "model-context-protocol",
28
- "cli",
28
+ "oauth",
29
+ "claude",
29
30
  "claude-code",
31
+ "claude-cowork",
32
+ "claude-connector",
33
+ "chatgpt",
34
+ "chatgpt-connector",
35
+ "cursor",
36
+ "windsurf",
37
+ "cli",
30
38
  "ai-agent",
31
39
  "ai-tools",
32
40
  "job-search",
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.kevinastuhuaman/trackly",
4
4
  "title": "Trackly CLI",
5
- "description": "Job search and application tracking for AI agents. Search 128K+ jobs across 1,900+ companies and 40+ ATS types. Filter by role, location, visa sponsorship. Natural language queries. Works with Claude Code, Cursor, Windsurf, and any MCP client.",
5
+ "description": "AI job search for Claude, ChatGPT, Cursor. 128K+ jobs, 1,900+ companies. OAuth or stdio.",
6
6
  "repository": {
7
7
  "url": "https://github.com/kevinastuhuaman/trackly-cli",
8
8
  "source": "github"
@@ -12,15 +12,17 @@
12
12
  {
13
13
  "src": "https://usetrackly.app/favicon-512x512.png?v=5",
14
14
  "mimeType": "image/png",
15
- "sizes": ["512x512"]
15
+ "sizes": [
16
+ "512x512"
17
+ ]
16
18
  }
17
19
  ],
18
- "version": "0.1.12",
20
+ "version": "0.2.1",
19
21
  "packages": [
20
22
  {
21
23
  "registryType": "npm",
22
24
  "identifier": "trackly-cli",
23
- "version": "0.1.12",
25
+ "version": "0.2.1",
24
26
  "runtimeHint": "npx",
25
27
  "runtimeArguments": [
26
28
  {
@@ -46,8 +48,8 @@
46
48
  "headers": [
47
49
  {
48
50
  "name": "Authorization",
49
- "description": "Bearer API key (trk_*). Create one at https://usetrackly.app/settings",
50
- "isRequired": true,
51
+ "description": "Optional Bearer trk_* API key for remote clients that don't support OAuth 2.1 Dynamic Client Registration. Clients that do support DCR can leave this blank \u2014 the server will guide them through an interactive OAuth flow. Create an API key at https://usetrackly.app/settings",
52
+ "isRequired": false,
51
53
  "isSecret": true
52
54
  }
53
55
  ]