Metadata-Version: 2.4
Name: pulselink-mcp
Version: 2.2.1
Summary: PulseLink — keyless open-web & social research source (MCP Server)
Author-email: Project Maintainers <maintainers@users.noreply.github.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Console
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: agent-connector-sdk[mcp]<1.0.0,>=0.2.1
Requires-Dist: python-dotenv>=1.2.4
Requires-Dist: requests>=2.34.2
Provides-Extra: mcp
Requires-Dist: agent-connector-sdk[mcp]<1.0.0,>=0.2.1; extra == "mcp"
Provides-Extra: youtube
Requires-Dist: yt-dlp>=2026.8.19; extra == "youtube"
Provides-Extra: feeds
Requires-Dist: feedparser>=6.0.14; extra == "feeds"
Provides-Extra: audio
Requires-Dist: faster-whisper>=1.2.1; extra == "audio"
Provides-Extra: sources
Requires-Dist: pulselink-mcp[feeds,youtube]>=2.2.1; extra == "sources"
Provides-Extra: all
Requires-Dist: pulselink-mcp[audio,feeds,mcp,youtube]>=2.2.0; extra == "all"
Provides-Extra: test
Requires-Dist: pytest-xdist>=3.8.0; extra == "test"
Requires-Dist: pytest>=9.1.1; extra == "test"
Requires-Dist: pytest-asyncio>=1.4.0; extra == "test"
Requires-Dist: pytest-cov>=7.1.0; extra == "test"
Requires-Dist: feedparser>=6.0.14; extra == "test"
Requires-Dist: yt-dlp>=2026.8.19; extra == "test"
Dynamic: license-file

# PulseLink MCP
## CLI or API | MCP

![PyPI - Version](https://img.shields.io/pypi/v/pulselink-mcp)
![MCP Server](https://badge.mcpx.dev?type=server 'MCP Server')
![PyPI - Downloads](https://img.shields.io/pypi/dd/pulselink-mcp)
![GitHub Repo stars](https://img.shields.io/github/stars/Knuckles-Team/pulselink-mcp)
![PyPI - License](https://img.shields.io/pypi/l/pulselink-mcp)
![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/Knuckles-Team/pulselink-mcp)

*Version: 2.2.1*

> **Documentation** — Installation, deployment, usage across the API, CLI, and MCP
> backing platform are maintained in the
> [official documentation](https://knuckles-team.github.io/pulselink-mcp/).

---

## Overview

**PulseLink MCP Server**

PulseLink — keyless open-web & social research source (MCP Server)

This repository is actively maintained - Contributions are welcome!

## MCP

### MCP Configuration Examples

<!-- MCP-CONFIG-EXAMPLES:START -->

> **Install the connector-focused `[mcp]` extra.** Examples use `pulselink-mcp[mcp]` to add
> FastMCP / FastAPI through `agent-connector-sdk`; the required Agent Connector SDK core
> still carries `epistemic-graph[full]`. The `[agent-runtime]` extra additionally
> enables model orchestration.

#### stdio Transport (local IDEs — Cursor, Claude Desktop, VS Code)

```json
{
  "mcpServers": {
    "pulselink-mcp": {
      "command": "uvx",
      "args": [
        "--from",
        "pulselink-mcp[mcp]",
        "pulselink-mcp"
      ],
      "env": {
        "MCP_TOOL_MODE": "intent",
        "PULSETOOL": "True"
      }
    }
  }
}
```

Runtime references require an alias-aware launcher such as Arbora. Other
launchers must omit those entries and inject the resolved values through their
own runtime secret boundary.

#### Streamable-HTTP Transport (networked / production)

```json
{
  "mcpServers": {
    "pulselink-mcp": {
      "command": "uvx",
      "args": [
        "--from",
        "pulselink-mcp[mcp]",
        "pulselink-mcp",
        "--transport",
        "streamable-http",
        "--port",
        "8000"
      ],
      "env": {
        "TRANSPORT": "streamable-http",
        "HOST": "127.0.0.1",
        "PORT": "8000",
        "MCP_TOOL_MODE": "intent",
        "PULSETOOL": "True"
      }
    }
  }
}
```

Alternatively, connect to a pre-deployed Streamable-HTTP instance by `url`:

```json
{
  "mcpServers": {
    "pulselink-mcp": {
      "url": "http://localhost:8000/pulselink-mcp/mcp"
    }
  }
}
```

Run a reviewed container image as a least-privilege stdio child (no
listener or published port):

```bash
docker run -i --rm \
  --read-only \
  --cap-drop=ALL \
  --security-opt=no-new-privileges \
  --pids-limit=256 \
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m \
  -e TRANSPORT=stdio \
  -e MCP_TOOL_MODE=intent \
  -e PULSETOOL=True \
  registry.example.invalid/pulselink-mcp@sha256:<digest> pulselink-mcp
```

For containerized network HTTP, supply an authenticated TLS ingress (or
direct server TLS), exact `MCP_ALLOWED_HOSTS`, and an exact trusted-proxy
CIDR policy through the operator-owned deployment profile. The generator
does not emit an unauthenticated non-loopback listener.

_Auto-generated from the code-read env surface (`MCP_TOOL_MODE` + package vars) — do not edit._
<!-- MCP-CONFIG-EXAMPLES:END -->

<!-- BEGIN GENERATED: additional-deployment-options -->
### Additional Deployment Options

`pulselink-mcp` can run as a local stdio process or container, or behind a remote
network boundary. The
[Deployment guide](https://knuckles-team.github.io/pulselink-mcp/deployment/) carries
the detailed transport contract.

- **Local container** — launch a reviewed immutable image as a least-privilege
  stdio child with no listener or published port.
- **Remote URL** — connect through an operator-supplied authenticated HTTPS
  ingress. Keep its URL, outbound identity references, trust profile, and exact
  `MCP_ALLOWED_HOSTS` in `AgentConfig`.
<!-- END GENERATED: additional-deployment-options -->

## Available MCP Tools

This table is auto-generated from the live server — do not edit by hand.

<!-- MCP-TOOLS-TABLE:START -->

#### Condensed action-routed tools (`MCP_TOOL_MODE=condensed`)

| MCP Tool | Toggle Env Var | Description |
|----------|----------------|-------------|
| `pulse_fetch` | `PULSETOOL` | Fetch one item (full text/body/transcript). CONCEPT:PK-OS.governance.search-fetch-list-transcribe |
| `pulse_ingest` | `PULSETOOL` | Search/list a source and natively ingest the results into epistemic-graph. |
| `pulse_list` | `PULSETOOL` | List items from a source channel/feed. CONCEPT:PK-OS.governance.search-fetch-list-transcribe |
| `pulse_search` | `PULSETOOL` | Search a source and return normalized documents. CONCEPT:PK-OS.governance.search-fetch-list-transcribe |
| `pulse_status` | `PULSETOOL` | Per-source backend + credential health (the doctor). CONCEPT:PK-OS.governance.search-fetch-list-transcribe |
| `pulse_transcribe` | `PULSETOOL` | Transcribe video/audio to text. CONCEPT:PK-OS.governance.audio-video-sources-transcript |

#### Verbose 1:1 API-mapped tools (`MCP_TOOL_MODE=verbose` or `both`)

<details>
<summary>6 per-operation tools — one per public API method (click to expand)</summary>

| MCP Tool | Toggle Env Var | Description |
|----------|----------------|-------------|
| `pulselink_fetch` | `PULSE_LINK_CLIENTTOOL` | Invoke the fetch operation. |
| `pulselink_list_items` | `PULSE_LINK_CLIENTTOOL` | Invoke the list_items operation. |
| `pulselink_search` | `PULSE_LINK_CLIENTTOOL` | Invoke the search operation. |
| `pulselink_sources` | `PULSE_LINK_CLIENTTOOL` | Invoke the sources operation. |
| `pulselink_status` | `PULSE_LINK_CLIENTTOOL` | Invoke the status operation. |
| `pulselink_transcribe` | `PULSE_LINK_CLIENTTOOL` | Invoke the transcribe operation. |

</details>

_6 action-routed tool(s) · 6 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (**`intent` default** — the six verb-tools, granular set loaded on demand · `condensed` action-routed · `verbose` 1:1 · `both`). Auto-generated — do not edit._
<!-- MCP-TOOLS-TABLE:END -->

## Environment Variables

<!-- ENV-VARS-TABLE:START -->

#### Package environment variables

| Variable | Example | Description |
|----------|---------|-------------|
| `HOST` | `0.0.0.0` |  |
| `PORT` | `8000` |  |
| `TRANSPORT` | `stdio` | options: stdio, streamable-http, sse |
| `TLS_PROFILE` | `private-pki` | TLS verification is mandatory. Select a named runtime profile from AgentConfig. |
| `TLS_PROFILES_REF` | `secret://runtime/tls-profiles` |  |
| `PULSELINK_TLS_PROFILE` | `private-pki` |  |
| `PULSELINK_TLS_PROFILE_REF` | `secret://runtime/pulselink-tls-profile` |  |
| `MODEL_TLS_PROFILE` | `public-default` |  |
| `MODEL_TLS_PROFILE_REF` | `secret://runtime/model-tls-profile` |  |
| `PULSELINK_MCP_MCP_IMAGE` | `registry.example/pulselink-mcp@sha256:<digest>` |  |
| `ENABLE_OTEL` | `True` |  |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:8080/api/public/otel` |  |
| `OTEL_EXPORTER_OTLP_PUBLIC_KEY_REF` | `secret://observability/otel/public-key` |  |
| `OTEL_EXPORTER_OTLP_SECRET_KEY_REF` | `secret://observability/otel/secret-key` |  |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` |  |
| `EUNOMIA_TYPE` | `none` | options: none, embedded, remote |
| `EUNOMIA_POLICY_FILE` | `mcp_policies.json` |  |
| `EUNOMIA_REMOTE_URL` | `http://eunomia-server:8000` |  |
| `PROVIDER` | — | operator-selected model provider |
| `MODEL_ID` | — | operator-selected model capability |
| `PULSETOOL` | `True` | These names match the authoritative "Toggle Env Var" column in the README MCP tools table (condensed action-routed surface). |
| `SOURCE_CREDENTIALS` | `{"x":{"type":"cookie_session","secret":"vault://pulselink/x/session"},"reddit":{"type":"oauth2","secret":"vault://pulselink/reddit/token","token_url":"https://www.reddit.com/api/v1/access_token","client_id":"<id>","client_secret_secret":"vault://pulselink/reddit/cs"},"github":{"type":"api_key","secret":"env://GITHUB_TOKEN","prefix":"token "},"exa":{"type":"api_key","secret":"env://EXA_API_KEY","prefix":""}}` | Keyless sources (youtube, web, rss, news, hackernews, v2ex, bilibili) need NOTHING here. Auth-laddered sources light up their higher-fidelity backend when a credential is set. SOURCE_CREDENTIALS is a JSON object mapping a source -> credential descriptor; secret values are URI refs resolved via the injected secrets backend (vault://, env://). Example: |

#### Inherited agent-connector-sdk variables (apply to every connector)

| Variable | Example | Description |
|----------|---------|-------------|
| `MCP_TOOL_MODE` | `intent` | Tool surface: `intent` \| `condensed` \| `verbose` \| `both` |
| `MCP_ENABLED_TOOLS` | — | Comma-separated tool allow-list |
| `MCP_DISABLED_TOOLS` | — | Comma-separated tool deny-list |
| `MCP_ENABLED_TAGS` | — | Comma-separated tag allow-list |
| `MCP_DISABLED_TAGS` | — | Comma-separated tag deny-list |
| `MCP_CLIENT_AUTH` | — | Outbound MCP child auth: `oidc-client-credentials` \| `basic` \| `none` |
| `OIDC_CLIENT_ID` | — | OIDC client id (service-account auth) |
| `OIDC_CLIENT_SECRET_REF` | `secret://identity/oidc-client-secret` | Runtime secret reference for the OIDC service account |
| `MCP_BASIC_AUTH_USERNAME` | — | HTTP Basic username (`MCP_CLIENT_AUTH=basic`) |
| `MCP_BASIC_AUTH_PASSWORD_REF` | `secret://identity/mcp-basic-password` | Runtime secret reference for HTTP Basic auth (`MCP_CLIENT_AUTH=basic`) |
| `DEBUG` | `False` | Verbose logging |
| `PYTHONUNBUFFERED` | `1` | Unbuffered stdout (recommended in containers) |
| `MCP_URL` | `http://localhost:8000/mcp` | URL of the MCP server the agent connects to |

_27 package + 14 inherited variable(s). Auto-generated from `.env.example` + the shared agent-connector-sdk set — do not edit._
<!-- ENV-VARS-TABLE:END -->

Every variable the server reads.

### MCP server / transport
| Variable | Description | Default |
|----------|-------------|---------|
| `TRANSPORT` | `stdio`, `streamable-http`, or `sse` | `stdio` |
| `HOST` | Bind host (HTTP transports) | `0.0.0.0` |
| `PORT` | Bind port (HTTP transports) | `8000` |
| `MCP_TOOL_MODE` | Tool surface: `condensed`, `verbose`, or `both` | `condensed` |
| `MCP_ENABLED_TOOLS` / `MCP_DISABLED_TOOLS` | Comma-separated tool allow/deny list | — |
| `MCP_ENABLED_TAGS` / `MCP_DISABLED_TAGS` | Comma-separated tag allow/deny list | — |
| `DEBUG` | Verbose logging | `False` |
| `PYTHONUNBUFFERED` | Unbuffered stdout (recommended in containers) | `1` |

### X / xAI Live Search (optional)
Used only by the optional X live-search capability. Its model identifier is
operator-supplied; keyless and non-model source paths do not require it.

| Variable | Description | Default |
|----------|-------------|---------|

### Source credentials (keyless-first)
| Variable | Description | Default |
|----------|-------------|---------|
| `SOURCE_CREDENTIALS` | JSON object mapping a source → credential descriptor (secret values are URI refs resolved via the secrets backend: `vault://`, `env://`, `sqlite://`). Keyless sources (youtube, web, rss, news, hackernews, v2ex, bilibili) need nothing here; auth-laddered sources (x, reddit, github, exa…) light up higher-fidelity backends when set. | — |

### Tool toggles
Each action-routed tool can be disabled individually via its toggle env var (set to `false`).
The full list is in the [Available MCP Tools](#available-mcp-tools) table above.

| Variable | Description | Default |
|----------|-------------|---------|
| `PULSETOOL` | Enable the PulseLink source tools (`pulse_search` / `pulse_list` / `pulse_fetch` / `pulse_transcribe` / `pulse_status`) | `True` |

### Telemetry & governance
| Variable | Description | Default |
|----------|-------------|---------|
| `ENABLE_OTEL` | Enable OpenTelemetry export | `True` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint | — |
| `OTEL_EXPORTER_OTLP_PUBLIC_KEY` / `OTEL_EXPORTER_OTLP_SECRET_KEY` | OTLP auth keys | — |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | OTLP protocol (e.g. `http/protobuf`) | — |
| `EUNOMIA_TYPE` | Authorization mode: `none`, `embedded`, `remote` | `none` |
| `EUNOMIA_POLICY_FILE` | Embedded policy file | `mcp_policies.json` |
| `EUNOMIA_REMOTE_URL` | Remote Eunomia server URL | — |

## Installation

Pick the extra that matches what the operator want to run:

| Extra | Installs | Use when |
|-------|----------|----------|
| `pulselink-mcp[mcp]` | Connector-focused MCP server (`agent-connector-sdk` — FastMCP/FastAPI + `epistemic-graph[full]`) | The operator only run the **MCP server** (smallest install / image) |
| `pulselink-mcp[all]` | Everything (`mcp` + `agent` + `logfire` + the `youtube`/`feeds`/`audio` source extras) | Development / both surfaces |

```bash
# Connector-focused MCP server (includes the shared graph engine)
uv pip install "pulselink-mcp[mcp]"

# Agent runtime (adds model orchestration to the shared graph engine)

# Everything (development)
uv pip install "pulselink-mcp[all]"      # or: python -m pip install "pulselink-mcp[all]"
```

> The optional source extras (`youtube` → `yt-dlp`, `feeds` → `feedparser`,
> `audio` → `faster-whisper`) are lazy-imported; the keyless web/forum/news/dev
> sources need none of them. Install `pulselink-mcp[sources]` for `youtube` + `feeds`.

### Container images (`:mcp`)

One multi-stage `docker/Dockerfile` builds the MCP-only image (`--target mcp`):

| Image tag | Build target | Contents | Entrypoint |
|-----------|--------------|----------|------------|
| `example/pulselink-mcp:mcp` | `--target mcp` | `pulselink-mcp[mcp]` — **connector-focused**, includes `epistemic-graph[full]`; no model-orchestration stack | `pulselink-mcp` |

```bash
docker build --target mcp   -t example/pulselink-mcp:mcp    docker/   # connector-focused MCP server
```

agent (`immutable agent digest`) with a co-located `:mcp` sidecar.

### Knowledge-graph database (`epistemic-graph`)

Agent Connector SDK core dependency (`epistemic-graph[full]`). The `[mcp]` extra keeps
deployments can use the bundled engine. For production or shared state, run
**epistemic-graph as a dedicated database service** and configure the runtime to use it.
Deployment recipes (single-node + Raft HA), connection configuration, and architecture
diagrams are documented in the
[epistemic-graph deployment guide](https://knuckles-team.github.io/epistemic-graph/deployment/).

<!-- BEGIN agent-connector-sdk-deployment (generated; do not edit between markers) -->

## Deploy with `agent-connector-sdk-deployment`

Provision this package with the consolidated **`agent-connector-sdk-deployment`**
workflow. It selects an installed-package, editable-source, or immutable-container
path; records only runtime secret and TLS-profile references in `AgentConfig`; and
runs doctor, registration, policy, observability, and rollback gates. Ask your agent
to **"deploy `pulselink-mcp` with agent-connector-sdk-deployment"**.

| Install mode | Command |
|------|---------|
| Installed package | `uv tool install "pulselink-mcp[mcp]"`, then run `pulselink-mcp` |
| Immutable container | deploy `registry.example.invalid/pulselink-mcp@sha256:<digest>` through the operator-selected orchestrator |

The repository embeds no deployment profile, credential value, certificate path, or
environment-specific endpoint. Supply those at runtime through `AgentConfig` and the
configured secret provider.

<!-- END agent-connector-sdk-deployment -->

<!-- GOVERNED-CAPABILITY:START -->
## Governed capability contract

This package ships a compact canonical skill surface with specialist procedures
kept as referenced workflows. The current MCP tools, skill metadata,
`connector_manifest.yml`, ontology, mappings, shapes, fixtures, migrations,
tool-schema fingerprints, and certification metadata form one versioned
capability contract. Validate them together; do not rely on stale tool names or
historical per-task skill wrappers.

Runtime endpoints, credentials, certificate trust, tenant identity, retention,
and observability policy are deployment inputs and are never packaged values.
See [Configuration, trust, and privacy](docs/configuration.md) before enabling a
network transport, connector ingestion, Arbora delegation, or trace export.
<!-- GOVERNED-CAPABILITY:END -->
