afpnews-mcp-server
by julesbonnard
README.md
# afpnews-mcp
MCP (Model Context Protocol) server that exposes [AFP](https://www.afp.com/) news content as tools for AI assistants. Works with any MCP-compatible client.
The package can also be used as a library without MCP server glue via `afpnews-mcp-server/definitions`.
## Prerequisites
- [Bun](https://bun.sh/) 1.3+
- An AFP API account (API key + username/password)
## Setup
```bash
git clone https://github.com/julesbonnard/afpnews-mcp-server.git
cd afpnews-mcp-server
bun install
```
Create a `.env` file:
```
APICORE_API_KEY=your-api-key
APICORE_USERNAME=your-username
APICORE_PASSWORD=your-password
```
## Usage
### Stdio transport (default)
For local MCP clients like Claude Code or Claude Desktop:
```json
{
"mcpServers": {
"afpnews": {
"command": "bun",
"args": ["src/index.ts"],
"cwd": "/absolute/path/to/afpnews-mcp-server",
"env": {
"APICORE_API_KEY": "your-api-key",
"APICORE_USERNAME": "your-username",
"APICORE_PASSWORD": "your-password"
}
}
}
}
```
### HTTP transport
For remote or multi-user deployments. Stateless: each request is served by a fresh MCP server
instance built from that request's own bearer token, so any number of instances can sit behind
a plain round-robin load balancer with no sticky sessions or shared store. Users authenticate via
OAuth2 PKCE using their AFP credentials.
Required environment variables for HTTP mode:
```
APICORE_API_KEY=your-api-key
APICORE_BASE_URL=https://api.afp.com
MCP_SERVER_URL=https://news-mcp.example.com
JWT_SECRET=a-random-string-of-at-least-32-characters
MCP_TRANSPORT=http
PORT=3000
```
Optional:
- `MCP_ALLOWED_REDIRECT_URIS` — comma-separated list of allowed OAuth redirect URIs.
`localhost`/`127.0.0.1` (any port) is always allowed regardless of this list —
it covers any client that runs a local OAuth callback server (Claude Code,
MCP Inspector, most IDEs). This var is the single place to allowlist clients
with a fixed, non-localhost callback URL. Known ones:
- Claude: `https://claude.ai/api/mcp/auth_callback`, `https://claude.com/api/mcp/auth_callback`
- ChatGPT: `https://chatgpt.com/connector_platform_oauth_redirect`, `https://chatgpt.com/oauth/callback`, `https://chat.openai.com/oauth/callback`
- Mistral (Le Chat): `https://callback.mistral.ai/v1/integrations_auth/oauth2_callback`
Add others as you connect new clients — the exact `redirect_uri` shows up in
the failed `/oauth/authorize` request when a client isn't allowlisted yet.
```bash
bun src/index.ts
```
If you expose the server remotely, use HTTPS.
### Docker
```bash
docker build -t afpnews-mcp .
docker run \
-e APICORE_API_KEY=your-api-key \
-e APICORE_BASE_URL=https://api.afp.com \
-e MCP_SERVER_URL=https://news-mcp.example.com \
-e JWT_SECRET=your-secret-32-chars-minimum \
-e MCP_TRANSPORT=http \
-p 3000:3000 \
afpnews-mcp
```
### Cloudflare Workers
The HTTP transport is built as a plain [Hono](https://hono.dev) app with no server-side state
(the OAuth authorization code and the bearer tokens are all self-contained), so it also runs as a
Cloudflare Worker via `src/http/worker.ts` — no KV, Durable Objects, or other bindings needed.
```bash
bunx wrangler login
bunx wrangler secret put APICORE_API_KEY
bunx wrangler secret put JWT_SECRET
```
Edit `wrangler.toml`'s `[vars]` (`APICORE_BASE_URL`, `MCP_SERVER_URL` — the latter must match your
actual `*.workers.dev` subdomain or custom domain), then:
```bash
bun run dev:worker # local dev server via workerd (bunx wrangler dev)
bun run deploy:worker # bunx wrangler deploy
```
Rate limiting isn't implemented in-process (it would be per-isolate and largely pointless on the
edge) — use [Cloudflare's own Rate Limiting](https://developers.cloudflare.com/waf/rate-limiting-rules/)
in front of the Worker instead.
### As a library (without MCP server dependency)
You can import pure definitions (tools, prompts, resources) and wire them into your own runtime:
```ts
import { AFP_DEFINITIONS } from 'afpnews-mcp-server/definitions';
const { tools, prompts, resources } = AFP_DEFINITIONS;
```
Or import each collection directly:
```ts
import {
TOOL_DEFINITIONS,
PROMPT_DEFINITIONS,
RESOURCE_DEFINITIONS,
} from 'afpnews-mcp-server/definitions';
```
Each definition is framework-agnostic:
- `tools`: `name`, `title`, `description`, `inputSchema`, `handler(apicore, args)`
- `prompts`: `name`, `title`, `description`, `argsSchema`, `handler(args)`
- `resources`: `name`, `uri`, `description`, `mimeType`, `handler()`
## Tools
| Tool | Description |
|------|-------------|
| `afp_search_articles` | Search AFP articles with filters, presets, and full-text mode |
| `afp_get_article` | Get a full article by its UNO identifier |
| `afp_find_similar` | Find similar articles (More Like This) from a UNO |
| `afp_list_facets` | List facet values (topics, genres, countries) with frequency counts |
| `afp_search_media` | Search AFP media documents (photos, videos, graphics) |
| `afp_get_media` | Get a full media document by UNO, with optional base64 image embed |
### Search presets
The `afp_search_articles` tool supports presets that apply predefined filters:
- **`a-la-une`** — Top story (French, last 24h)
- **`agenda`** — Upcoming events
- **`previsions`** — Editorial planning / forecasts
- **`major-stories`** — Major articles
### List preset
- **`trending-topics`** — Trending topics from the last 24h
### Full text
By default, `afp_search_articles` returns excerpts (first 2 paragraphs). Set `fullText: true` to get the complete article body. Presets default to full text.
### Pagination
Use `offset` to paginate through results (e.g. `offset: 10` to skip the first 10). For large chronological scans, prefer narrowing `dateFrom`/`dateTo` ranges in `facets` over high offsets. Keep `size` small (10–20) for best performance.
## Prompts
| Prompt | Description |
|--------|-------------|
| `daily-briefing` | Generate a daily news briefing |
| `comprehensive-analysis` | In-depth analysis on a topic |
| `factcheck` | Verify facts using AFP factchecks |
| `country-news` | News summary for a specific country |
## Resources
| Resource | Description |
|----------|-------------|
| `afp://topics` | AFP Stories topic catalog by language |
## Development
```bash
bun install
bun test
```
## Internal Architecture
- `src/tools/*.ts`, `src/prompts/*.ts`, `src/resources/*.ts` contain pure definitions.
- `src/tools/index.ts`, `src/prompts/index.ts`, `src/resources/index.ts` contain MCP registration glue.
- `src/definitions.ts` exports aggregated, server-agnostic definitions:
- `AFP_DEFINITIONS`
- `TOOL_DEFINITIONS`
- `PROMPT_DEFINITIONS`
- `RESOURCE_DEFINITIONS`
## License
ISC
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessResponsive