Skip to main content
Glama
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
```