Skip to content

Commit 936d2e1

Browse files
committed
feat(mcp-outline): init package & module
1 parent 37004f9 commit 936d2e1

3 files changed

Lines changed: 239 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
> [!IMPORTANT]
2+
> This Module _might_ not have all the capabilities you'd want / expect. Please raise an [issue](https://github.com/74k1/tixpkgs/issues) or figure out a fix for a PR. :)
3+
>
4+
> Contributions are always welcome!
5+
6+
# `nixosModules'.services.mcp-outline`
7+
8+
**mcp-outline** is a Model Context Protocol server for interacting with Outline.
9+
10+
## Info
11+
12+
- Project Source: `https://github.com/Vortiago/mcp-outline`
13+
- Project Docs: `https://github.com/Vortiago/mcp-outline/blob/main/docs/configuration.md`
14+
15+
## Usage
16+
17+
```nix
18+
{
19+
inputs,
20+
...
21+
}: {
22+
imports = [
23+
inputs.tixpkgs.nixosModules'.services.mcp-outline
24+
# or
25+
inputs.tixpkgs.nixosModules."services/mcp-outline"
26+
];
27+
28+
services.mcp-outline = {
29+
enable = true;
30+
31+
settings = {
32+
MCP_TRANSPORT = "streamable-http";
33+
MCP_HOST = "127.0.0.1";
34+
MCP_PORT = 3000;
35+
OUTLINE_API_URL = "https://outline.example.com/api";
36+
};
37+
38+
environmentFile = ./mcp-outline.env; # Requires OUTLINE_API_KEY (use agenix / sops-nix)
39+
40+
# only needed if you want direct network access instead of a reverse proxy
41+
openFirewall = false;
42+
};
43+
}
44+
```
45+
46+
Example `mcp-outline.env`:
47+
48+
```env
49+
OUTLINE_API_KEY=your-api-key
50+
```
51+
52+
## Notes
53+
54+
The service must run in `sse` or `streamable-http` mode.
55+
56+
`stdio` is intended for client-spawned local MCP processes and will be rejected by the module.
57+
58+
Most upstream configuration can be passed through `services.mcp-outline.settings` using the original environment variable names. ()
59+
60+
Do not put secrets there; those values are written to the Nix store.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
inherit (lib)
9+
getExe
10+
mkEnableOption
11+
mkIf
12+
mkOption
13+
mkPackageOption
14+
types
15+
;
16+
17+
cfg = config.services.mcp-outline;
18+
19+
settingsFormat = pkgs.formats.keyValue { };
20+
generatedEnvironmentFile = settingsFormat.generate "mcp-outline-env" cfg.settings;
21+
in
22+
{
23+
options.services.mcp-outline = {
24+
enable = mkEnableOption "mcp-outline service";
25+
26+
package = mkPackageOption pkgs "mcp-outline" { };
27+
28+
environmentFile = mkOption {
29+
type = types.path;
30+
default = "/dev/null";
31+
example = "/run/secrets/mcp-outline.env";
32+
description = ''
33+
Path to an environment file loaded by the service.
34+
35+
Use this for secrets like `OUTLINE_API_KEY` so they stay out of the Nix store.
36+
'';
37+
};
38+
39+
settings = mkOption {
40+
type = settingsFormat.type;
41+
default = {
42+
MCP_TRANSPORT = "streamable-http";
43+
MCP_HOST = "127.0.0.1";
44+
MCP_PORT = 3000;
45+
};
46+
example = {
47+
MCP_TRANSPORT = "streamable-http";
48+
MCP_HOST = "0.0.0.0";
49+
MCP_PORT = 3000;
50+
OUTLINE_URL = "https://outline.example.com";
51+
};
52+
description = ''
53+
Environment variables written to a generated environment file for mcp-outline.
54+
55+
Common values are `MCP_TRANSPORT`, `MCP_HOST`, `MCP_PORT`, and `OUTLINE_URL`.
56+
Put secrets such as `OUTLINE_API_KEY` in `services.mcp-outline.environmentFile` instead.
57+
58+
Also see: https://github.com/Vortiago/mcp-outline/blob/main/docs/configuration.md
59+
'';
60+
};
61+
62+
openFirewall = mkOption {
63+
type = types.bool;
64+
default = false;
65+
description = "Whether to open the configured MCP port in the firewall.";
66+
};
67+
};
68+
69+
config = mkIf cfg.enable {
70+
assertions = [
71+
{
72+
assertion = cfg.settings.MCP_TRANSPORT != "stdio";
73+
message = "services.mcp-outline.settings.MCP_TRANSPORT must be \"sse\" or \"streamable-http\" when running mcp-outline as a NixOS service.";
74+
}
75+
];
76+
77+
systemd.services.mcp-outline = {
78+
description = "mcp-outline service";
79+
after = [ "network.target" ];
80+
wantedBy = [ "multi-user.target" ];
81+
restartTriggers = [
82+
cfg.package
83+
cfg.environmentFile
84+
generatedEnvironmentFile
85+
];
86+
87+
serviceConfig = {
88+
Type = "simple";
89+
DynamicUser = true;
90+
ExecStart = getExe cfg.package;
91+
Restart = "on-failure";
92+
EnvironmentFile = [
93+
cfg.environmentFile
94+
generatedEnvironmentFile
95+
];
96+
97+
StateDirectory = "mcp-outline";
98+
NoNewPrivileges = true;
99+
PrivateTmp = true;
100+
ProtectSystem = "strict";
101+
ProtectHome = true;
102+
ProtectControlGroups = true;
103+
ProtectKernelModules = true;
104+
ProtectKernelTunables = true;
105+
RestrictSUIDSGID = true;
106+
LockPersonality = true;
107+
MemoryDenyWriteExecute = true;
108+
SystemCallArchitectures = "native";
109+
};
110+
};
111+
112+
networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.settings.MCP_PORT;
113+
};
114+
}

pkgs/mc/mcp-outline/default.nix

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
lib,
3+
fetchFromGitHub,
4+
python3Packages,
5+
}:
6+
7+
python3Packages.buildPythonApplication rec {
8+
pname = "mcp-outline";
9+
version = "1.8.0";
10+
pyproject = true;
11+
12+
src = fetchFromGitHub {
13+
owner = "Vortiago";
14+
repo = "mcp-outline";
15+
tag = "v${version}";
16+
hash = "sha256-3QjwnoEfregVCNlC24qczGWUOo4zQbcwLkh0e6F9hxc=";
17+
};
18+
19+
build-system = with python3Packages; [
20+
setuptools
21+
setuptools-scm
22+
pythonRelaxDepsHook
23+
];
24+
25+
dependencies = with python3Packages; [
26+
mcp
27+
httpx
28+
python-dotenv
29+
];
30+
31+
pythonRelaxDeps = [
32+
"mcp"
33+
];
34+
35+
nativeCheckInputs = with python3Packages; [
36+
pytestCheckHook
37+
pytest-asyncio
38+
pytest-cov-stub
39+
anyio
40+
];
41+
42+
# Integration and e2e tests require a running MCP server / Docker stack
43+
disabledTestPaths = [
44+
"tests/e2e"
45+
];
46+
47+
pytestFlagsArray = [
48+
"-m"
49+
"'not integration and not e2e'"
50+
"--ignore=tests/features/test_dynamic_tools.py"
51+
];
52+
53+
env.SETUPTOOLS_SCM_PRETEND_VERSION = version;
54+
55+
pythonImportsCheck = [ "mcp_outline" ];
56+
57+
meta = {
58+
description = "A Model Context Protocol (MCP) server for Outline";
59+
homepage = "https://github.com/Vortiago/mcp-outline";
60+
changelog = "https://github.com/Vortiago/mcp-outline/releases/tag/${src.tag}";
61+
license = lib.licenses.mit;
62+
maintainers = [ "74k1" ];
63+
mainProgram = "mcp-outline";
64+
};
65+
}

0 commit comments

Comments
 (0)