newsblur-cli 0.1.0__tar.gz
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.
- newsblur_cli-0.1.0/.gitignore +95 -0
- newsblur_cli-0.1.0/Dockerfile +13 -0
- newsblur_cli-0.1.0/PKG-INFO +122 -0
- newsblur_cli-0.1.0/README.md +92 -0
- newsblur_cli-0.1.0/newsblur_mcp/__init__.py +0 -0
- newsblur_cli-0.1.0/newsblur_mcp/__main__.py +10 -0
- newsblur_cli-0.1.0/newsblur_mcp/auth.py +106 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/__init__.py +75 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/auth.py +327 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/__init__.py +0 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/account.py +26 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/actions.py +124 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/auth.py +74 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/discover.py +83 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/feeds.py +140 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/intelligence.py +117 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/commands/stories.py +174 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/output.py +392 -0
- newsblur_cli-0.1.0/newsblur_mcp/cli/runner.py +50 -0
- newsblur_cli-0.1.0/newsblur_mcp/client.py +65 -0
- newsblur_cli-0.1.0/newsblur_mcp/prompts/__init__.py +0 -0
- newsblur_cli-0.1.0/newsblur_mcp/prompts/prompts.py +85 -0
- newsblur_cli-0.1.0/newsblur_mcp/resources/__init__.py +0 -0
- newsblur_cli-0.1.0/newsblur_mcp/resources/resources.py +85 -0
- newsblur_cli-0.1.0/newsblur_mcp/server.py +78 -0
- newsblur_cli-0.1.0/newsblur_mcp/settings.py +29 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/__init__.py +0 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/account.py +51 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/actions.py +172 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/briefing.py +55 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/classifiers.py +115 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/discovery.py +60 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/feeds.py +300 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/notifications.py +53 -0
- newsblur_cli-0.1.0/newsblur_mcp/tools/stories.py +282 -0
- newsblur_cli-0.1.0/newsblur_mcp/transforms.py +128 -0
- newsblur_cli-0.1.0/newsblur_mcp/ui.py +402 -0
- newsblur_cli-0.1.0/pyproject.toml +48 -0
- newsblur_cli-0.1.0/tests/__init__.py +0 -0
- newsblur_cli-0.1.0/tests/conftest.py +42 -0
- newsblur_cli-0.1.0/tests/test_transforms.py +74 -0
- newsblur_cli-0.1.0/uv.lock +1482 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
logs/*.log
|
|
2
|
+
logs/*.log.*
|
|
3
|
+
logs/*.pid
|
|
4
|
+
logs/celerybeat-schedule.db
|
|
5
|
+
logs/celerybeat-schedule-*.db
|
|
6
|
+
*.pyc
|
|
7
|
+
backup.db
|
|
8
|
+
__pycache__/
|
|
9
|
+
static/*
|
|
10
|
+
local_settings.py
|
|
11
|
+
celerybeat-schedule
|
|
12
|
+
celerybeat.pid
|
|
13
|
+
media/iphone/NewsBlur/build
|
|
14
|
+
media/iphone/build
|
|
15
|
+
build/
|
|
16
|
+
certbot.conf
|
|
17
|
+
.DS_Store
|
|
18
|
+
**/*.perspectivev*
|
|
19
|
+
.vscode/*
|
|
20
|
+
.env
|
|
21
|
+
task_env.py
|
|
22
|
+
app_env.py
|
|
23
|
+
data/
|
|
24
|
+
api/ip_addresses.txt
|
|
25
|
+
.prom_cache
|
|
26
|
+
config/certificates
|
|
27
|
+
**/*.xcuserstate
|
|
28
|
+
UserInterfaceState.xcuserstate
|
|
29
|
+
UserInterfaceState\.xcuserstate
|
|
30
|
+
*.xcuserstate
|
|
31
|
+
xcuserdata
|
|
32
|
+
.xcodeproj/ push.xcodeproj/project.pbxproj
|
|
33
|
+
clients/ios/NewsBlur.xcodeproj/project.xcworkspace/xcshareddata/
|
|
34
|
+
*.mode1v3
|
|
35
|
+
*.pbxuser
|
|
36
|
+
media/maintenance.html
|
|
37
|
+
media/maintenance.html.unused
|
|
38
|
+
config/settings
|
|
39
|
+
static.tgz
|
|
40
|
+
.sass-cache
|
|
41
|
+
media/css/circular/.sass-cache
|
|
42
|
+
media/css/circular/sass/.sass-cache
|
|
43
|
+
media/css/circular
|
|
44
|
+
config/settings
|
|
45
|
+
config/secrets
|
|
46
|
+
ansible/roles/sentry/vars/secrets.yml
|
|
47
|
+
templates/maintenance_on.html
|
|
48
|
+
apps/social/spam.py
|
|
49
|
+
venv*
|
|
50
|
+
/backup
|
|
51
|
+
/backups
|
|
52
|
+
config/mongodb_keyfile.key
|
|
53
|
+
|
|
54
|
+
# Docker Jinja templates
|
|
55
|
+
docker/haproxy/haproxy.consul.cfg
|
|
56
|
+
# docker/haproxy/haproxy.staging.cfg # Staging doesn't use jinja templates, so no need to ignore
|
|
57
|
+
# Conductor workspace files (auto-generated by .conductor/conductor-setup.sh)
|
|
58
|
+
.conductor/docker-compose.*.yml
|
|
59
|
+
.conductor/haproxy/
|
|
60
|
+
# Ignore all subdirs in .conductor
|
|
61
|
+
.conductor/**/*
|
|
62
|
+
|
|
63
|
+
# Worktree development files (auto-generated by worktree-dev.sh)
|
|
64
|
+
.worktree/
|
|
65
|
+
docker/nginx/nginx.consul.conf
|
|
66
|
+
docker/prometheus/prometheus.yml
|
|
67
|
+
docker/redis/redis_replica.conf
|
|
68
|
+
docker/redis/redis_*_replica.conf
|
|
69
|
+
docker/postgres/postgres.conf
|
|
70
|
+
|
|
71
|
+
# Local configuration file (sdk path, etc)
|
|
72
|
+
/originals
|
|
73
|
+
/node/originals
|
|
74
|
+
media/safari/NewsBlur.safariextz
|
|
75
|
+
|
|
76
|
+
# Discovery cache files (generated by discover_* management commands)
|
|
77
|
+
apps/discover/fixtures/*_cache.json
|
|
78
|
+
|
|
79
|
+
# IDE files
|
|
80
|
+
/docker/volumes/*
|
|
81
|
+
|
|
82
|
+
**/node_modules
|
|
83
|
+
*.tfstate*
|
|
84
|
+
.terraform*
|
|
85
|
+
grafana.ini
|
|
86
|
+
apps/api/ip_addresses.txt
|
|
87
|
+
.ansible/
|
|
88
|
+
.dev_session
|
|
89
|
+
.conductor
|
|
90
|
+
.worktree
|
|
91
|
+
docs
|
|
92
|
+
plans
|
|
93
|
+
|
|
94
|
+
# PyPI build artifacts
|
|
95
|
+
newsblur_mcp/dist/
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: newsblur-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NewsBlur CLI and MCP server - manage feeds, stories, and classifiers
|
|
5
|
+
Project-URL: Homepage, https://newsblur.com/features/mcp-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/samuelclay/NewsBlur
|
|
7
|
+
Project-URL: Issues, https://github.com/samuelclay/NewsBlur/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: cli,feeds,mcp,news,newsblur,rss
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
21
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: typer>=0.15.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: respx>=0.22; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# NewsBlur CLI
|
|
32
|
+
|
|
33
|
+
A command-line interface for [NewsBlur](https://newsblur.com), the visual intelligence RSS reader. Read feeds, manage stories, train classifiers, and more from your terminal.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install newsblur-cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Log in (opens your browser for OAuth)
|
|
45
|
+
newsblur auth login
|
|
46
|
+
|
|
47
|
+
# Get your daily briefing
|
|
48
|
+
newsblur briefing
|
|
49
|
+
|
|
50
|
+
# List your feeds
|
|
51
|
+
newsblur feeds list
|
|
52
|
+
|
|
53
|
+
# Read stories from a feed
|
|
54
|
+
newsblur stories river --feed 42
|
|
55
|
+
|
|
56
|
+
# Search stories
|
|
57
|
+
newsblur stories search "machine learning"
|
|
58
|
+
|
|
59
|
+
# Save a story
|
|
60
|
+
newsblur save <story-hash>
|
|
61
|
+
|
|
62
|
+
# Mark stories as read
|
|
63
|
+
newsblur read <story-hash>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Commands
|
|
67
|
+
|
|
68
|
+
| Command | Description |
|
|
69
|
+
|---------|-------------|
|
|
70
|
+
| `newsblur auth login` | Log in via OAuth |
|
|
71
|
+
| `newsblur auth logout` | Log out and remove credentials |
|
|
72
|
+
| `newsblur auth status` | Show authentication status |
|
|
73
|
+
| `newsblur briefing` | Daily briefing of top stories |
|
|
74
|
+
| `newsblur stories river` | Read stories from feeds or folders |
|
|
75
|
+
| `newsblur stories search` | Search across stories |
|
|
76
|
+
| `newsblur feeds list` | List subscribed feeds |
|
|
77
|
+
| `newsblur feeds add` | Subscribe to a new feed |
|
|
78
|
+
| `newsblur feeds remove` | Unsubscribe from a feed |
|
|
79
|
+
| `newsblur train show` | View intelligence classifiers |
|
|
80
|
+
| `newsblur train set` | Train a classifier |
|
|
81
|
+
| `newsblur discover` | Find new feeds by topic |
|
|
82
|
+
| `newsblur account` | Show account info |
|
|
83
|
+
| `newsblur save` | Save a story |
|
|
84
|
+
| `newsblur read` | Mark a story as read |
|
|
85
|
+
|
|
86
|
+
## Self-Hosted
|
|
87
|
+
|
|
88
|
+
For self-hosted NewsBlur instances, pass `--server`:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
newsblur auth login --server https://nb.example.com
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The server URL is persisted to `~/.config/newsblur/config.json`.
|
|
95
|
+
|
|
96
|
+
## Output Formats
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Rich terminal output (default)
|
|
100
|
+
newsblur feeds list
|
|
101
|
+
|
|
102
|
+
# JSON output (for scripting)
|
|
103
|
+
newsblur --json feeds list
|
|
104
|
+
|
|
105
|
+
# Raw text output
|
|
106
|
+
newsblur --raw feeds list
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## MCP Server
|
|
110
|
+
|
|
111
|
+
This package also includes an MCP (Model Context Protocol) server for AI assistants. See [NewsBlur MCP](https://newsblur.com/features/mcp-cli) for details.
|
|
112
|
+
|
|
113
|
+
## Requirements
|
|
114
|
+
|
|
115
|
+
- Python 3.11+
|
|
116
|
+
- A NewsBlur account (premium required for most features)
|
|
117
|
+
|
|
118
|
+
## Links
|
|
119
|
+
|
|
120
|
+
- [NewsBlur](https://newsblur.com)
|
|
121
|
+
- [GitHub](https://github.com/samuelclay/NewsBlur)
|
|
122
|
+
- [MCP & CLI Features](https://newsblur.com/features/mcp-cli)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# NewsBlur CLI
|
|
2
|
+
|
|
3
|
+
A command-line interface for [NewsBlur](https://newsblur.com), the visual intelligence RSS reader. Read feeds, manage stories, train classifiers, and more from your terminal.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install newsblur-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Log in (opens your browser for OAuth)
|
|
15
|
+
newsblur auth login
|
|
16
|
+
|
|
17
|
+
# Get your daily briefing
|
|
18
|
+
newsblur briefing
|
|
19
|
+
|
|
20
|
+
# List your feeds
|
|
21
|
+
newsblur feeds list
|
|
22
|
+
|
|
23
|
+
# Read stories from a feed
|
|
24
|
+
newsblur stories river --feed 42
|
|
25
|
+
|
|
26
|
+
# Search stories
|
|
27
|
+
newsblur stories search "machine learning"
|
|
28
|
+
|
|
29
|
+
# Save a story
|
|
30
|
+
newsblur save <story-hash>
|
|
31
|
+
|
|
32
|
+
# Mark stories as read
|
|
33
|
+
newsblur read <story-hash>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
| Command | Description |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| `newsblur auth login` | Log in via OAuth |
|
|
41
|
+
| `newsblur auth logout` | Log out and remove credentials |
|
|
42
|
+
| `newsblur auth status` | Show authentication status |
|
|
43
|
+
| `newsblur briefing` | Daily briefing of top stories |
|
|
44
|
+
| `newsblur stories river` | Read stories from feeds or folders |
|
|
45
|
+
| `newsblur stories search` | Search across stories |
|
|
46
|
+
| `newsblur feeds list` | List subscribed feeds |
|
|
47
|
+
| `newsblur feeds add` | Subscribe to a new feed |
|
|
48
|
+
| `newsblur feeds remove` | Unsubscribe from a feed |
|
|
49
|
+
| `newsblur train show` | View intelligence classifiers |
|
|
50
|
+
| `newsblur train set` | Train a classifier |
|
|
51
|
+
| `newsblur discover` | Find new feeds by topic |
|
|
52
|
+
| `newsblur account` | Show account info |
|
|
53
|
+
| `newsblur save` | Save a story |
|
|
54
|
+
| `newsblur read` | Mark a story as read |
|
|
55
|
+
|
|
56
|
+
## Self-Hosted
|
|
57
|
+
|
|
58
|
+
For self-hosted NewsBlur instances, pass `--server`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
newsblur auth login --server https://nb.example.com
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The server URL is persisted to `~/.config/newsblur/config.json`.
|
|
65
|
+
|
|
66
|
+
## Output Formats
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Rich terminal output (default)
|
|
70
|
+
newsblur feeds list
|
|
71
|
+
|
|
72
|
+
# JSON output (for scripting)
|
|
73
|
+
newsblur --json feeds list
|
|
74
|
+
|
|
75
|
+
# Raw text output
|
|
76
|
+
newsblur --raw feeds list
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## MCP Server
|
|
80
|
+
|
|
81
|
+
This package also includes an MCP (Model Context Protocol) server for AI assistants. See [NewsBlur MCP](https://newsblur.com/features/mcp-cli) for details.
|
|
82
|
+
|
|
83
|
+
## Requirements
|
|
84
|
+
|
|
85
|
+
- Python 3.11+
|
|
86
|
+
- A NewsBlur account (premium required for most features)
|
|
87
|
+
|
|
88
|
+
## Links
|
|
89
|
+
|
|
90
|
+
- [NewsBlur](https://newsblur.com)
|
|
91
|
+
- [GitHub](https://github.com/samuelclay/NewsBlur)
|
|
92
|
+
- [MCP & CLI Features](https://newsblur.com/features/mcp-cli)
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Entry point for `python -m newsblur_mcp`.
|
|
2
|
+
|
|
3
|
+
Imports from the canonical module to avoid the double-import problem
|
|
4
|
+
that occurs when `python -m` loads the module as __main__ while tool
|
|
5
|
+
sub-modules import from newsblur_mcp.server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from newsblur_mcp.server import main
|
|
9
|
+
|
|
10
|
+
main()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""NewsBlur OAuth provider for FastMCP.
|
|
2
|
+
|
|
3
|
+
Proxies authentication through Django's existing OAuth2 infrastructure
|
|
4
|
+
so that MCP clients (Claude Code, Codex, etc.) can authenticate via
|
|
5
|
+
standard OAuth flows without any manual token setup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from fastmcp.server.auth import TokenVerifier
|
|
15
|
+
from fastmcp.server.auth.auth import AccessToken
|
|
16
|
+
from fastmcp.server.auth.oauth_proxy import OAuthProxy
|
|
17
|
+
from pydantic import AnyHttpUrl
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
from newsblur_mcp.settings import (
|
|
22
|
+
MCP_OAUTH_BASE_URL,
|
|
23
|
+
MCP_OAUTH_CLIENT_ID,
|
|
24
|
+
MCP_OAUTH_CLIENT_SECRET,
|
|
25
|
+
MCP_OAUTH_INTERNAL_URL,
|
|
26
|
+
MCP_OAUTH_UPSTREAM_URL,
|
|
27
|
+
)
|
|
28
|
+
from newsblur_mcp.ui import patch_fastmcp_ui
|
|
29
|
+
|
|
30
|
+
# Replace FastMCP's generic OAuth pages with NewsBlur-branded ones
|
|
31
|
+
patch_fastmcp_ui()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NewsBlurTokenVerifier(TokenVerifier):
|
|
35
|
+
"""Validates upstream Django OAuth tokens by calling NewsBlur's API."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, upstream_base_url: str, timeout_seconds: int = 10):
|
|
38
|
+
super().__init__(required_scopes=None)
|
|
39
|
+
self.upstream_base_url = upstream_base_url
|
|
40
|
+
self.timeout_seconds = timeout_seconds
|
|
41
|
+
|
|
42
|
+
async def verify_token(self, token: str) -> AccessToken | None:
|
|
43
|
+
"""Verify a Django OAuth access token by calling the user info endpoint."""
|
|
44
|
+
try:
|
|
45
|
+
async with httpx.AsyncClient(timeout=self.timeout_seconds) as client:
|
|
46
|
+
response = await client.get(
|
|
47
|
+
f"{self.upstream_base_url}/oauth/user/info/",
|
|
48
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if response.status_code != 200:
|
|
52
|
+
logger.warning("Django token verification failed: status=%d url=%s", response.status_code, self.upstream_base_url)
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
user_info = response.json()
|
|
56
|
+
logger.info("Token verified for user=%s", user_info.get("user_name", "unknown"))
|
|
57
|
+
|
|
58
|
+
return AccessToken(
|
|
59
|
+
token=token,
|
|
60
|
+
client_id=MCP_OAUTH_CLIENT_ID,
|
|
61
|
+
scopes=["read", "write", "mcp"],
|
|
62
|
+
expires_at=int(time.time()) + (60 * 60 * 24 * 365 * 10),
|
|
63
|
+
claims={
|
|
64
|
+
"sub": str(user_info.get("user_id", "")),
|
|
65
|
+
"username": user_info.get("user_name", ""),
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error("Django token verification error: %s", e)
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class NewsBlurOAuthProvider(OAuthProxy):
|
|
74
|
+
"""OAuth provider that proxies to Django's OAuth2 infrastructure."""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
*,
|
|
79
|
+
base_url: AnyHttpUrl | str | None = None,
|
|
80
|
+
upstream_url: str | None = None,
|
|
81
|
+
internal_url: str | None = None,
|
|
82
|
+
client_id: str | None = None,
|
|
83
|
+
client_secret: str | None = None,
|
|
84
|
+
):
|
|
85
|
+
base_url = base_url or MCP_OAUTH_BASE_URL
|
|
86
|
+
upstream_url = upstream_url or MCP_OAUTH_UPSTREAM_URL
|
|
87
|
+
internal_url = internal_url or MCP_OAUTH_INTERNAL_URL
|
|
88
|
+
client_id = client_id or MCP_OAUTH_CLIENT_ID
|
|
89
|
+
client_secret = client_secret or MCP_OAUTH_CLIENT_SECRET
|
|
90
|
+
|
|
91
|
+
token_verifier = NewsBlurTokenVerifier(upstream_base_url=internal_url)
|
|
92
|
+
|
|
93
|
+
super().__init__(
|
|
94
|
+
# Browser redirect — user sees this URL
|
|
95
|
+
upstream_authorization_endpoint=f"{upstream_url}/oauth/authorize/",
|
|
96
|
+
# Server-to-server — bypasses TLS for self-signed certs in dev
|
|
97
|
+
upstream_token_endpoint=f"{internal_url}/oauth/token/",
|
|
98
|
+
upstream_client_id=client_id,
|
|
99
|
+
upstream_client_secret=client_secret,
|
|
100
|
+
token_verifier=token_verifier,
|
|
101
|
+
base_url=base_url,
|
|
102
|
+
issuer_url=base_url,
|
|
103
|
+
require_authorization_consent=False,
|
|
104
|
+
forward_pkce=False,
|
|
105
|
+
valid_scopes=["read", "write", "mcp"],
|
|
106
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""NewsBlur CLI - read feeds, manage stories, and train classifiers from your terminal."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
app = typer.Typer(
|
|
8
|
+
name="newsblur",
|
|
9
|
+
help="NewsBlur CLI - read feeds, manage stories, and train classifiers from your terminal.",
|
|
10
|
+
no_args_is_help=True,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.callback(invoke_without_command=True)
|
|
15
|
+
def main(
|
|
16
|
+
ctx: typer.Context,
|
|
17
|
+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
18
|
+
raw_output: bool = typer.Option(False, "--raw", help="Output unformatted text"),
|
|
19
|
+
version: bool = typer.Option(False, "--version", "-v", help="Show version"),
|
|
20
|
+
server: str = typer.Option(
|
|
21
|
+
None, "--server", "-s",
|
|
22
|
+
help="NewsBlur server URL (default: https://newsblur.com). Persisted to config.",
|
|
23
|
+
),
|
|
24
|
+
):
|
|
25
|
+
"""NewsBlur CLI - read feeds, manage stories, and train classifiers."""
|
|
26
|
+
ctx.ensure_object(dict)
|
|
27
|
+
ctx.obj["json"] = json_output
|
|
28
|
+
ctx.obj["raw"] = raw_output
|
|
29
|
+
if server:
|
|
30
|
+
from newsblur_mcp.cli.auth import set_server_url
|
|
31
|
+
set_server_url(server)
|
|
32
|
+
if version:
|
|
33
|
+
try:
|
|
34
|
+
from importlib.metadata import version as get_version
|
|
35
|
+
|
|
36
|
+
typer.echo(f"newsblur {get_version('newsblur-cli')}")
|
|
37
|
+
except Exception:
|
|
38
|
+
typer.echo("newsblur 0.1.0")
|
|
39
|
+
raise typer.Exit()
|
|
40
|
+
if ctx.invoked_subcommand is None:
|
|
41
|
+
typer.echo(ctx.get_help())
|
|
42
|
+
raise typer.Exit()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Register all command groups
|
|
46
|
+
from newsblur_mcp.cli.commands import account as account_commands
|
|
47
|
+
from newsblur_mcp.cli.commands import actions as actions_commands
|
|
48
|
+
from newsblur_mcp.cli.commands import auth as auth_commands
|
|
49
|
+
from newsblur_mcp.cli.commands import discover as discover_commands
|
|
50
|
+
from newsblur_mcp.cli.commands import feeds as feeds_commands
|
|
51
|
+
from newsblur_mcp.cli.commands import intelligence as intelligence_commands
|
|
52
|
+
from newsblur_mcp.cli.commands import stories as stories_commands
|
|
53
|
+
|
|
54
|
+
app.add_typer(auth_commands.app, name="auth", help="Login, logout, and check auth status")
|
|
55
|
+
app.add_typer(stories_commands.app, name="stories", help="Read, search, and browse stories")
|
|
56
|
+
app.add_typer(feeds_commands.app, name="feeds", help="List, add, and manage feed subscriptions")
|
|
57
|
+
app.add_typer(
|
|
58
|
+
actions_commands.app,
|
|
59
|
+
name="actions",
|
|
60
|
+
help="Mark read, save, unsave, share stories",
|
|
61
|
+
)
|
|
62
|
+
app.add_typer(
|
|
63
|
+
intelligence_commands.app,
|
|
64
|
+
name="train",
|
|
65
|
+
help="View and train intelligence classifiers",
|
|
66
|
+
)
|
|
67
|
+
app.add_typer(discover_commands.app, name="discover", help="Find new feeds by topic or similarity")
|
|
68
|
+
|
|
69
|
+
# Top-level shortcuts for common actions
|
|
70
|
+
app.command("account")(account_commands.account_info)
|
|
71
|
+
app.command("briefing")(stories_commands.briefing)
|
|
72
|
+
app.command("read")(actions_commands.mark_read)
|
|
73
|
+
app.command("save")(actions_commands.save)
|
|
74
|
+
app.command("unsave")(actions_commands.unsave)
|
|
75
|
+
app.command("share")(actions_commands.share)
|