OpenVisio BYO MCP Bridge
by talibackend
README.md
# OpenVisio BYO MCP Bridge
A Model Context Protocol (MCP) server that exposes OpenVisio's team collaboration API as a set of tools for AI agents. Built with [Mastra](https://mastra.ai).
The bridge turns OpenVisio's REST API (tickets, board columns, channels, documents, projects, activity) into 15 MCP tools that agents can call to read team state, communicate, and drive tickets autonomously.
## Setup
Requires Node.js >= 22.13.
```bash
npm install
cp .env.example .env
# edit .env and set OPENVISIO_API_URL if not using the default
```
### Environment variables
| Variable | Description |
| --- | --- |
| `OPENVISIO_API_URL` | API base URL (defaults to the dev gateway) |
| `PORT` | HTTP port for the MCP server (default `4112`) |
### Authentication
The bridge authenticates every backend request as a registered OpenVisio agent by sending the `x-agent-identifier` and `x-agent-api-key` headers, which the backend's `userAuthMiddleware` accepts alongside the bearer-JWT flow used by frontend clients.
Agent credentials are **not** configured via env — they are passed on **every tool invocation** via the `agent_identifier` and `agent_api_key` arguments (alongside the numeric `agent_id` used for identity resolution). To provision an agent:
1. Create the agent as an admin: `POST /agents` with `{ name, type: "bring_your_own", user_id }`.
2. Copy the `identifier` and `api_key` from the create response.
3. Set the agent to `status: "active"` via `PUT /agents/{id}` — inactive agents are rejected.
4. Pass the credentials on each tool call.
## Running
```bash
npm run start:mcp # standalone MCP server on http://localhost:4112/mcp
npm run dev # Mastra dev (Studio)
npm run build # Mastra build
```
## Tools
| Tool | What it does | Backed by |
| --- | --- | --- |
| `list_resources` | Team resources as `(kind, id, title)` refs; optional `product_id` scope | `/projects`, per-collection lists |
| `get_resource` | One resource's full content as Markdown | `GET` entity endpoints |
| `get_context` | Role-scoped team briefing (projects, columns, tickets, channels, docs, activity) | Aggregated |
| `search` | Full-text search across the team | Fan-out over per-collection `search` params |
| `get_marching_orders` | Role-aware autonomy instructions; call first in a cycle | Aggregated |
| `poll_inbox` | Actionable items (assigned/claimable tickets, mentions); returns a cursor | Aggregated |
| `ack_inbox` | Advance the inbox watermark | Local state |
| `post_message` | Post to a channel as the agent (≥ Commenter) | `POST .../channels/{id}/messages` |
| `react_message` | Add an emoji reaction | **No-op** (no backend endpoint) |
| `list_my_tasks` | Assigned tickets + board columns (`todo`/`active`/`done`) | `/tasks`, `/tasks/task-types` |
| `claim_ticket` | Assign an unassigned ticket to the agent, move to first active column (≥ Editor) | `GET`+`PUT .../tasks/{id}` |
| `update_ticket` | Move/edit a ticket (status, priority, description, assignee, title, due date) (≥ Editor) | `PUT .../tasks/{id}` |
| `comment_ticket` | Add a comment to a ticket | **No-op** (no backend endpoint) |
| `create_ticket` | File a new ticket (≥ Editor) | `POST .../tasks` |
| `create_api_request` | Save a request into the team's API collection | **No-op** (no backend endpoint) |
> Board columns are the API's *task types*. "Status" of a ticket = its column id (`type_id`) + `position`.
### Notes / limitations
- **No-op tools** (`react_message`, `comment_ticket`, `create_api_request`) are registered so the tool contract stays intact; they succeed without side effects. The backend has no endpoints for reactions, task comments, or an API collection.
- **Threads**: `post_message` accepts an optional `parent_id`, but the backend does not officially document thread support; it may be ignored.
- **Identity**: self-aware tools require `agent_id` (the registered OpenVisio agent id). The bridge derives the agent's `user_id` and org role from the agent and member records.
- **Inbox cursors** (`poll_inbox`/`ack_inbox`) are held in memory and keyed by agent; they reset when the bridge restarts.
- **Response parsing** is defensive: the API documents request shapes but not response shapes beyond the `{ ok, message, status, body }` envelope, so field extraction is best-effort.
## Architecture
```
src/
lib/
config.ts # env config (API URL only) + .env loading
client.ts # fetch client: per-request agent-header auth, envelope unwrap, pagination, tolerant pick()
identity.ts # resolve agent -> user_id -> member role
markdown.ts # per-kind Markdown renderers
cursors.ts # in-memory inbox watermark store
tools/
read.ts # list_resources, get_resource, get_context, search
autonomy.ts # get_marching_orders, poll_inbox, ack_inbox
messaging.ts # post_message, react_message
tickets.ts # list_my_tasks, claim_ticket, update_ticket, comment_ticket, create_ticket
api_tool.ts # create_api_request
mastra/
index.ts # Mastra instance
mcp/server.ts # MCPServer with the 15-tool registry
server.ts # standalone HTTP MCP endpoint (/mcp)
```
## Development
```bash
npx tsc --noEmit # typecheck
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing