feat: add docker setup
This commit is contained in:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
!src/
|
||||
!migrations/
|
||||
!build.rs
|
||||
!Cargo.toml
|
||||
!Cargo.lock
|
||||
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -376,6 +376,16 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.8.0"
|
||||
@@ -1193,6 +1203,16 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -1561,6 +1581,7 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
|
||||
@@ -19,7 +19,7 @@ dirs = "6"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "chrono", "migrate"] }
|
||||
tokio = { version = "1", features = ["rt", "macros"] }
|
||||
tokio = { version = "1", features = ["rt", "macros", "io-std", "io-util", "signal"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
|
||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM rust:1.85 AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
ENV SQLX_OFFLINE=true
|
||||
RUN cargo build --release --bin todo-mcp
|
||||
|
||||
FROM gcr.io/distroless/cc-debian12
|
||||
COPY --from=build /src/target/release/todo-mcp /
|
||||
ENTRYPOINT ["/todo-mcp"]
|
||||
32
README.md
32
README.md
@@ -62,10 +62,18 @@ todo-mcp tags
|
||||
todo-mcp purge
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```sh
|
||||
docker build -t local/todo-mcp .
|
||||
docker run --rm -v todo-mcp-data:/data -e TODO_DB=/data/todos.db local/todo-mcp --help
|
||||
```
|
||||
|
||||
Data is persisted in the `todo-mcp-data` volume. See [Usage](#usage) for more commands.
|
||||
|
||||
## Claude Setup
|
||||
|
||||
todo-mcp ships with an MCP server that lets Claude manage your todos. Add this
|
||||
to your Claude MCP config:
|
||||
### Native
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -78,5 +86,25 @@ to your Claude MCP config:
|
||||
}
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Build the image first (see [Docker](#docker)), then add to your Claude MCP config:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"todo": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run", "-i", "--rm",
|
||||
"-v", "todo-mcp-data:/data",
|
||||
"-e", "TODO_DB=/data/todos.db",
|
||||
"local/todo-mcp", "mcp-serve"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This gives Claude access to all todo operations — adding, listing, completing,
|
||||
editing, removing, and purging.
|
||||
|
||||
17
src/mcp.rs
17
src/mcp.rs
@@ -1,8 +1,9 @@
|
||||
use std::io::{self, BufRead, Write};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
|
||||
use crate::db::{self, ListFilters, Pool};
|
||||
use crate::model::Priority;
|
||||
@@ -233,11 +234,19 @@ async fn dispatch(pool: &Pool, name: &str, args: &Value) -> Value {
|
||||
}
|
||||
|
||||
pub async fn serve(pool: &Pool) -> Result<()> {
|
||||
let stdin = io::stdin().lock();
|
||||
let stdin = BufReader::new(tokio::io::stdin());
|
||||
let mut stdout = io::stdout().lock();
|
||||
let mut lines = stdin.lines();
|
||||
|
||||
loop {
|
||||
let line: String = tokio::select! {
|
||||
line = lines.next_line() => match line? {
|
||||
Some(line) => line,
|
||||
None => break,
|
||||
},
|
||||
_ = tokio::signal::ctrl_c() => break,
|
||||
};
|
||||
|
||||
for line in stdin.lines() {
|
||||
let line = line?;
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user