A path, resolved against this Officer, so the agent reaches /api/openseo/mcp through the same gate as everything else and never the instance directly. Not wrapped as Officer-native tools: the vocabulary moves upstream, a copy would go stale, and it fails silently — a tool exists that Officer cannot see.
85 lines
3.9 KiB
TypeScript
85 lines
3.9 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
/**
|
|
* OpenSEO — self-hosted keyword research, rank tracking and site audits, inside Officer.
|
|
*
|
|
* ── It points at an instance; it does not own one ──
|
|
*
|
|
* OpenSEO is a container, and a plugin has no primitive for "bring a container": `osDependencies`
|
|
* installs OS packages. So this follows the shape `jellyfin`, `gitea` and `proxy-manager` already use —
|
|
* a stored connection to a service running somewhere, with the credential write-only across the
|
|
* boundary.
|
|
*
|
|
* A decision to revisit rather than a permanent one. `todo/infra/opm-officer-s-own-process-manager` is
|
|
* heading toward the Docker network, and on the day a plugin can declare a container, the connection
|
|
* becomes something this provisions rather than something somebody types.
|
|
*
|
|
* ── The permission split, and why it is not the same as proxy-manager's ──
|
|
*
|
|
* Proxy Manager carves out `/_config` because its credential can redirect any hostname on the machine.
|
|
* The shape here is the same and the reason is different: OpenSEO self-hosts with
|
|
* `AUTH_MODE=local_noauth`, so ANY reachable request to it is unauthenticated by design — the whole of
|
|
* Officer's gate is the authentication. A member who could read the connection could reach the instance
|
|
* directly and bypass this plugin entirely.
|
|
*
|
|
* So `/_config` is owner-only, and the pass-through is the only door. Nothing here may publish
|
|
* OpenSEO's own port, and that is not a hardening measure — it is the security model upstream
|
|
* documents: *"only expose it behind your own auth-protected reverse proxy, tunnel, or private
|
|
* network."*
|
|
*
|
|
* ── Why the tools are not reimplemented here ──
|
|
*
|
|
* OpenSEO ships its own MCP server at `/mcp`. Most integrations need Officer to invent the agent-facing
|
|
* surface; this one arrives with one, built for exactly this kind of client. Wrapping it in
|
|
* Officer-native tools is a real option and an open decision — but inventing a second vocabulary for
|
|
* tools that already exist would be work whose only product is drift.
|
|
*/
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '0.1.0',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'OpenSEO',
|
|
summary: 'Keyword research, rank tracking, competitors, backlinks and site audits — self-hosted',
|
|
/*
|
|
`Marketing` and `Research` would both be more accurate and neither exists — the union is closed, and
|
|
extending it is a platform change rather than a plugin's to make. `Development` is the least wrong of
|
|
what is there: this is a tool about websites, used by whoever builds them. Worth revisiting if the
|
|
store ever grows a marketing shelf.
|
|
*/
|
|
tags: ['Development', 'Homelab'],
|
|
icon: 'TrendingUp',
|
|
color: '#22c55e',
|
|
|
|
/*
|
|
OpenSEO's own MCP server, handed to the agent as-is.
|
|
|
|
A PATH, resolved against this Officer — so the agent reaches `/api/openseo/mcp`, through the same
|
|
gate as everything else, and never the instance directly. Which matters more here than elsewhere:
|
|
upstream self-hosts unauthenticated by design, so the proxy is the authentication.
|
|
|
|
Not wrapped as Officer-native tools, deliberately. The vocabulary moves upstream; a copy would go
|
|
stale and fail silently, with a tool existing that Officer cannot see.
|
|
*/
|
|
mcp: { path: '/mcp' },
|
|
|
|
permissions: [
|
|
{
|
|
key: 'openseo',
|
|
label: 'OpenSEO',
|
|
description: 'Keyword research, rank tracking, competitor and backlink data, and site audits',
|
|
|
|
/*
|
|
The connection form, the probe, and the DataForSEO key.
|
|
|
|
Owner-only by PREFIX, so `/_config/test` is covered too. That matters here for the same reason it
|
|
does in proxy-manager: the test request carries a credential in its body, and a member who can
|
|
call it has an oracle pointed at somebody else's key.
|
|
*/
|
|
ownerOnly: ['/_config'],
|
|
},
|
|
],
|
|
};
|
|
|
|
export default manifest;
|