Skip to main content
Glama
README.md
# Line Chrome MCP

Local gateway that turns the LINE Chrome Extension UI into a stable automation surface:

- REST API (`/v1`)
- Server-Sent Events for gateway events (`/v1/events`)
- MCP Streamable HTTP (`/mcp`, including SSE streaming when the protocol uses a stream)
- MCP stdio (`line-mcp`) implemented as a thin proxy over the local REST gateway

The DOM/CDP adapter is intentionally hidden behind the messaging core. MCP clients and REST callers never depend on LINE CSS class names or DOM implementation details.

## Status

This is an initial working prototype built from the observed LINE Extension DOM contract. It supports:

- chat listing/search
- reading text/image/sticker message metadata
- virtualized chat/message scrolling
- incoming message events for the currently rendered active chat
- visible chat-list change events
- queued/idempotent text sending with post-send message-ID verification
- REST + MCP stdio + MCP Streamable HTTP

Not yet implemented: reply sending, file/image upload, reactions, durable storage, and full background message capture for chats that are never rendered by the LINE UI.

## Requirements

- Node.js 20+
- Chromium/Chrome with the LINE Chrome Extension installed and logged in
- a dedicated Chrome profile launched with DevTools remote debugging enabled

Chrome 136+ does not allow remote debugging against the normal default user-data directory. Use a dedicated profile.

Example:

```bash
google-chrome \
  --user-data-dir="$HOME/.line-mcp-profile" \
  --remote-debugging-port=9222
```

Open LINE in that profile and log in.

The adapter connects to Chrome through CDP; it does not discover a standalone
LINE process. The LINE page must be an actual `chrome-extension://...` page in
the remotely-debugged profile. A `view-source:chrome-extension://...` tab is a
DevTools/source wrapper and is not a usable LINE target.

## Install

```bash
npm install
npm run build
```

Export `.env.example` values in the gateway process or process manager as
needed. This prototype reads `process.env` directly and does not load a `.env`
file automatically. The defaults are:

```text
LINE_CDP_URL=http://127.0.0.1:9222
LINE_EXTENSION_ID=ophjlpahpchlmihnnnihgmmeilfjmjjc
LINE_GATEWAY_HOST=127.0.0.1
LINE_GATEWAY_PORT=8787
```

`LINE_EXTENSION_ID` can be omitted; the adapter will then look for a Chrome extension page containing LINE-specific ARIA/DOM markers.

Run the test suite before starting the gateway:

```bash
npm run check
```

## Start the gateway

```bash
npm start
```

Development mode:

```bash
npm run dev
```

Endpoints:

```text
REST API:           http://127.0.0.1:8787/v1
OpenAPI metadata:   http://127.0.0.1:8787/openapi.json
Event SSE:          http://127.0.0.1:8787/v1/events
MCP HTTP:           http://127.0.0.1:8787/mcp
```

### Codex CLI registration

The stdio server is a proxy and requires the gateway to be running first. Add
it to Codex with an absolute Node.js path so it does not depend on an
interactive shell loading NVM:

```bash
codex mcp add line \
  --env LINE_GATEWAY_URL=http://127.0.0.1:8787 \
  -- /absolute/path/to/node /absolute/path/to/Line-Chrome-MCP/dist/src/mcp/stdio.js

codex mcp get line
```

If Chrome uses another CDP port, restart the gateway with the matching URL:

```bash
LINE_CDP_URL=http://127.0.0.1:9223 npm start
```

The gateway port and the Chrome CDP port are independent. `LINE_GATEWAY_URL`
points the stdio proxy at the gateway; `LINE_CDP_URL` points the gateway at
Chrome.

## REST API

### Health / status

```bash
curl http://127.0.0.1:8787/v1/health
curl http://127.0.0.1:8787/v1/status
curl http://127.0.0.1:8787/v1/capabilities
```

### Chats

```bash
curl 'http://127.0.0.1:8787/v1/chats?limit=50'
curl 'http://127.0.0.1:8787/v1/chats?q=Pai'
curl 'http://127.0.0.1:8787/v1/chats?unread=true'
```

`line_search_chats` and `/v1/chats?q=...` search chat titles only. They do not
perform a global full-text search across every message. To inspect message
content, first resolve a chat ID and then call the messages endpoint. If a name
matches multiple chats, keep the candidates and ask the user to disambiguate.

### Messages

```bash
curl 'http://127.0.0.1:8787/v1/chats/CHAT_ID/messages?limit=50'
```

Older page:

```bash
curl 'http://127.0.0.1:8787/v1/chats/CHAT_ID/messages?limit=50&before=MESSAGE_ID'
```

### Send text

```bash
curl -X POST 'http://127.0.0.1:8787/v1/chats/CHAT_ID/messages' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: workflow-run-123' \
  -d '{"text":"ทดสอบส่งจาก API"}'
```

The request returns `202 Accepted` with an operation. Poll it:

```bash
curl http://127.0.0.1:8787/v1/operations/OPERATION_ID
```

UI write operations are serialized so two agents cannot switch LINE chats and type at the same time.

## Gateway event SSE

```bash
curl -N http://127.0.0.1:8787/v1/events
```

Current event families include:

```text
message.created
message.sent
chat.updated
operation.created
operation.started
operation.succeeded
operation.failed
```

`message.created` is generated for newly rendered messages in the active chat. `chat.updated` is generated when a visible chat-row preview/unread state changes.

## MCP Streamable HTTP

Use:

```text
http://127.0.0.1:8787/mcp
```

This uses the current MCP Streamable HTTP server entry. Streaming responses use SSE when required by MCP. It is not the deprecated legacy standalone HTTP+SSE transport.

Tools:

```text
line_get_status
line_get_capabilities
line_search_chats
line_get_messages
line_send_message
line_get_operation
```

## MCP stdio

The stdio process does not own Chrome or LINE. It calls the already-running gateway over localhost, so multiple MCP hosts do not create competing LINE sessions.

```bash
LINE_GATEWAY_URL=http://127.0.0.1:8787 node dist/src/mcp/stdio.js
```

Example MCP host configuration:

```json
{
  "mcpServers": {
    "line": {
      "command": "node",
      "args": ["/absolute/path/to/Line-Chrome-MCP/dist/src/mcp/stdio.js"],
      "env": {
        "LINE_GATEWAY_URL": "http://127.0.0.1:8787"
      }
    }
  }
}
```

The compiled entry points live under `dist/src/` because the TypeScript project
includes both `src/` and `test/` with `rootDir` set to `.`. Use the package
scripts and `bin` entries rather than assuming `dist/index.js` or
`dist/mcp/stdio.js`.

## Diagnostics and troubleshooting

Check all three layers independently:

```bash
curl http://127.0.0.1:9222/json/version
curl http://127.0.0.1:9222/json/list
curl http://127.0.0.1:8787/v1/health
curl http://127.0.0.1:8787/v1/status
```

Interpret `/v1/status` as follows:

- `ready: true`: the gateway found a LINE page and its expected DOM markers.
- `connected: true, ready: false`: CDP is reachable, but the target is not a
  usable LINE page. Check the profile, port, login state, and page URL.
- `connected: false`: the gateway cannot connect to the configured CDP URL.

The LINE extension normally appears as renderer/extension child processes of
Chrome, not as a process named `line`. Process listings alone cannot prove that
the gateway can use it; `/json/list` must expose the extension page and the
gateway must be pointed at that same Chrome instance.

For the incident-driven setup notes and recovery checklist, see
[`docs/lessons-learned.md`](docs/lessons-learned.md).

## Authentication / remote binding

By default the service binds to `127.0.0.1` without authentication.

If `LINE_GATEWAY_HOST` is changed to a non-loopback address, startup is refused unless `LINE_GATEWAY_TOKEN` is set.

Then use:

```text
Authorization: Bearer <token>
```

The same token is used by `line-mcp` via `LINE_GATEWAY_TOKEN`.

## Architecture

```text
LINE Chrome Extension
        │
        │ DOM / CDP
        ▼
ChromeLineAdapter
        │
        ▼
MessagingGateway
  ├─ serialized UI queue
  ├─ operations + idempotency
  └─ event bus
        │
        ├─ REST /v1
        ├─ SSE /v1/events
        └─ MCP /mcp

MCP stdio client
        │ stdin/stdout
        ▼
line-mcp thin proxy
        │ REST localhost
        ▼
MessagingGateway
```

## DOM strategy

The adapter avoids generated CSS module names wherever possible and targets observed semantic markers such as:

```text
button[aria-label="Go chatroom"]
div[data-is-dropzone="true"][data-mid]
[data-message-id]
[data-message-select-id]
[data-timestamp]
[data-is-message-text="true"]
textarea-ex[placeholder="Enter a message"]
```

LINE uses virtualized lists, so chat enumeration and history reads scroll and deduplicate by stable IDs instead of assuming the entire history exists in `document.body` at once.

## Security note

This project automates an already logged-in personal LINE client. Treat the gateway as sensitive local software. Do not expose it to a network without authentication and additional access controls appropriate to your environment.

TDQS

A3.9/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a distinct concern: status, capabilities, chat search, message reading, message sending, and async operation polling. Even get_status and get_capabilities are clearly separated by describing gateway/CDP state versus adapter feature implementation.

Naming Consistency5/5

All tools share the line_ prefix and follow a consistent verb_noun snake_case pattern: get_status, get_capabilities, search_chats, get_messages, send_message, get_operation. This makes the toolset highly predictable.

Tool Count5/5

Six tools is well-scoped for a LINE automation adapter: status, capabilities, chat discovery, message retrieval, sending, and async operation tracking. Each tool earns its place without redundancy or bloat.

Completeness4/5

The toolset covers the core LINE automation workflow: discover chats, read messages, send messages, and track async operations. Minor gaps exist around live message subscription/streaming and cancellation of queued operations, but these are not fatal for the apparent purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues