Skip to main content
Glama
dalvivishal

Sharebox MCP Server

by dalvivishal

Sharebox MCP Server

A Model Context Protocol (MCP) server that exposes a Sharebox-style project / case / file-management system to any MCP-capable AI client (Claude Desktop, Claude Code, Cursor, or a custom agent). Every write operation is recorded in an immutable audit trail, so an LLM can create cases and register file uploads while leaving a complete forensic record.

Built with FastMCP + SQLAlchemy + SQLite.


Table of Contents


Related MCP server: Trust Chain MCP

Why this exists

Document and case management systems (legal discovery, insurance claims, compliance workflows) are exactly the kind of tool an AI assistant should be able to drive — "how many open cases are on project 7?", "log this file against case 42" — but they are also exactly the kind of system where an unattributed mutation is unacceptable.

This server addresses both halves:

  1. Read and write tools are exposed over MCP, so an LLM can query and mutate the system through a typed, self-describing interface instead of scraping a UI.

  2. Every mutation writes an audit_logs row alongside the data change, capturing the action, the table, the record ID, and a JSON payload of the details.


Architecture

+----------------------+
|  MCP Client          |   Claude Desktop / Claude Code / Cursor / custom agent
|  (the LLM host)      |
+----------+-----------+
           |  JSON-RPC over stdio (MCP transport)
           v
+----------------------+
|  app/mcp_server.py   |   FastMCP("ShareboxMCPServer")
|  @mcp.tool()         |   4 registered tools; each opens and closes its own DB session
+----------+-----------+
           v
+----------------------+
|  app/crud.py         |   business logic + add_entrust_logs() audit hook
+----------+-----------+
           v
+----------------------+
|  app/models.py       |   SQLAlchemy ORM
|  app/database.py     |   engine / SessionLocal / Base
+----------+-----------+
           v
     sharebox.db  (SQLite)

The server speaks stdio transport by default, which is what desktop MCP clients launch and pipe into. There is no HTTP surface unless you add one.


Data model

Four tables, defined in app/models.py:

projects

Column

Type

Notes

id

Integer

primary key

name

String

indexed, required

description

String

created_at

DateTime(tz)

server_default=now()

cases

Column

Type

Notes

id

Integer

primary key

project_id

Integer

FK to projects.id, required

title

String

indexed, required

status

String

defaults to "open"

created_at

DateTime(tz)

file_uploads

Column

Type

Notes

id

Integer

primary key

case_id

Integer

FK to cases.id, required

filename

String

required

file_size

Integer

bytes

uploaded_at

DateTime(tz)

audit_logs

Column

Type

Notes

id

Integer

primary key

action

String

e.g. "CREATE"

table_name

String

e.g. "cases"

record_id

Integer

primary key of the mutated row

details

String

JSON blob of the relevant fields

timestamp

DateTime(tz)

Relationships: Project 1-N Case 1-N FileUpload. AuditLog is deliberately not foreign-keyed to anything — it stores table_name + record_id as loose references, so a log row survives even if the target row is later removed.


MCP tools

All four are declared in app/mcp_server.py. The docstring on each function is what the LLM sees as the tool description, so it is part of the interface.

get_project_summary(project_id: int) -> dict

Read-only. Returns project metadata plus case counts.

{
  "project_id": 1,
  "name": "Project Alpha",
  "description": "A confidential test project",
  "total_cases": 3,
  "open_cases": 2
}

Returns {"error": "Project not found"} if the ID does not exist.

list_recent_file_uploads(days: int = 1) -> list[dict]

Read-only. Returns every upload with uploaded_at >= now() - days.

[
  {
    "id": 1,
    "filename": "evidence.pdf",
    "case_id": 1,
    "file_size": 10240,
    "uploaded_at": "2026-09-15 08:14:22"
  }
]

create_new_case(project_id: int, title: str, description: str = "") -> dict

Mutating — audited. Validates that the parent project exists, creates the case with status="open", and writes a CREATE / cases audit row.

{
  "success": true,
  "case_id": 12,
  "title": "Case 100",
  "message": "Case created and securely audited."
}

On a bad project_id it returns {"error": "Project with ID N does not exist."} rather than raising — MCP clients handle a structured error better than a stack trace.

The description parameter is accepted by the tool but is not currently persisted: the cases table has no description column. Add one to models.py and thread it through crud.create_case() if you need it stored.

upload_file_metadata(case_id: int, filename: str, file_size: int) -> dict

Mutating — audited. Validates that the parent case exists, records the metadata, and writes a CREATE / file_uploads audit row.

{
  "success": true,
  "file_upload_id": 5,
  "message": "File metadata uploaded and securely audited."
}

This tool records metadata only. No file bytes cross the MCP boundary. Actual file transfer is expected to happen out-of-band; this server tracks that it happened.

Not exposed as a tool

crud.create_project() exists and is audited, but is intentionally not registered as an MCP tool — creating top-level projects is treated as an administrative action reserved for a human. Register it with @mcp.tool() if you want to change that policy.


Installation

Requires Python 3.10+ (the tool signatures use list[dict] builtin generics).

cd CustomMCPServer

python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate

pip install -r requirements.txt

Dependencies (requirements.txt): fastmcp, fastapi, sqlalchemy, pydantic, uvicorn.

Initialize the database

python run.py --init-db

This runs Base.metadata.create_all() and produces sharebox.db in the working directory. It is idempotent — safe to re-run; it will not drop existing tables.


Running the server

python run.py

With no flags, run.py calls mcp.run(), which serves MCP over stdio. You will see no output and the process will appear to hang — that is correct. It is waiting for JSON-RPC frames on stdin. Stop it with Ctrl+C.

You can also use the FastMCP CLI:

fastmcp run app/mcp_server.py:mcp

Connecting an MCP client

Claude Desktop / Claude Code

Add the server to your MCP config (claude_desktop_config.json, or via claude mcp add in Claude Code):

{
  "mcpServers": {
    "sharebox": {
      "command": "/absolute/path/to/CustomMCPServer/venv/bin/python",
      "args": ["/absolute/path/to/CustomMCPServer/run.py"],
      "cwd": "/absolute/path/to/CustomMCPServer"
    }
  }
}

On Windows, use venv\\Scripts\\python.exe as the command.

cwd matters. The SQLite URL is the relative path sqlite:///./sharebox.db, so the server resolves the database against whatever directory it was launched from. Point cwd at the project root, or you will silently get a fresh, empty database.

Restart the client. The four tools should appear in its tool list, and you can then ask things like "Summarize project 1" or "Open a new case on project 1 called Discovery Batch 3."


Testing

test_client.py is a self-contained end-to-end check of the CRUD and audit layers. It bypasses MCP and drives crud.py directly.

python test_client.py

Warning: the script begins with Base.metadata.drop_all() — it destroys the contents of sharebox.db. Never run it against a database you care about.

It exercises the full path (create project, create case, upload file metadata, read summary, list uploads) and then asserts that exactly 3 audit rows were written, exiting with status 1 if the count is wrong.

Expected tail:

6. Verifying Audit Logs...
   Total Audit Logs: 3
   - Action: CREATE, Table: projects, Record ID: 1, Details: {"name": "Project Alpha"}
   - Action: CREATE, Table: cases, Record ID: 1, Details: {"title": "Case 100", "project_id": 1}
   - Action: CREATE, Table: file_uploads, Record ID: 1, Details: {...}
SUCCESS: 3 audit logs successfully recorded.

Project layout

CustomMCPServer/
├── app/
│   ├── __init__.py
│   ├── crud.py          # business logic + audit logging
│   ├── database.py      # engine, SessionLocal, Base, get_db()
│   ├── mcp_server.py    # FastMCP instance + the 4 @mcp.tool() definitions
│   └── models.py        # SQLAlchemy ORM models
├── requirements.txt
├── run.py               # entrypoint: --init-db, or serve MCP over stdio
├── test_client.py       # destructive end-to-end CRUD/audit test
└── .gitignore

Design notes

Audit logging is a caller-committed hook. log_activity() calls db.add() but deliberately does not commit — the comment in app/crud.py states the intent: the caller commits, so the data change and the audit row land atomically.

In the current code the create functions commit the entity first, then log, then commit again. A crash between those two commits would leave an entity with no audit row. If you need a hard guarantee, restructure to a single commit:

db.add(db_case)
db.flush()                      # assigns db_case.id without committing
add_entrust_logs(db, "CREATE", "cases", db_case.id, {...})
db.commit()                     # one atomic transaction

add_entrust_logs is an alias. It forwards to log_activity() verbatim; the alias exists to satisfy an external naming requirement. Either name works.

Session per tool call. Each @mcp.tool() opens a SessionLocal() and closes it in a finally block. Tools are therefore safe to call concurrently, and no session outlives a single request.

check_same_thread=False is set on the SQLite connection because FastMCP may dispatch tool calls from a thread other than the one that created the engine.

Tool-level error handling. Mutating tools catch ValueError from the CRUD layer and return {"error": "..."}. This is deliberate: an LLM can read and recover from a returned error object, whereas a raised exception becomes an opaque protocol-level failure.

list_recent_file_uploads uses naive UTC. The cutoff is computed with datetime.utcnow() and compared against a timezone-aware column. This works on SQLite, which stores naive timestamps, but will need an aware cutoff (datetime.now(timezone.utc)) if you move to PostgreSQL.


Extending the server

Add a tool — decorate a function in app/mcp_server.py:

@mcp.tool()
def close_case(case_id: int) -> dict:
    """Close an open case. This action is strictly logged in the audit trail."""
    db = get_db_session()
    try:
        case = crud.close_case(db, case_id)          # add this to crud.py
        return {"success": True, "case_id": case.id}
    except ValueError as e:
        return {"error": str(e)}
    finally:
        db.close()

Write the docstring carefully — it is the only description the LLM gets.

Audit a new mutation — call add_entrust_logs(db, action, table_name, record_id, details) inside the CRUD function before the final commit. The details dict is JSON-serialized, so keep it to primitives.

Switch databases — change SQLALCHEMY_DATABASE_URL in app/database.py (e.g. postgresql+psycopg://user:pass@host/db) and drop the SQLite-only connect_args.


Troubleshooting

Symptom

Cause / fix

Client lists the server but shows zero tools

The process crashed at import. Run python run.py manually and read the traceback — stdio transport hides startup errors in the client UI.

no such table: projects

You skipped python run.py --init-db, or cwd points elsewhere so a blank sharebox.db was created.

Summary always reports "Project not found"

Two different sharebox.db files. Confirm the cwd in your MCP config matches where you ran --init-db.

TypeError: 'type' object is not subscriptable

Python older than 3.10. Upgrade, or rewrite list[dict] as List[Dict] from typing.

Test passes but the server sees no data

test_client.py drops and recreates all tables. Re-seed after running it.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to sign decisions with post-quantum cryptographic proofs and maintain secure audit trails for compliance. It provides tools for stamping events, verifying chain integrity, and exporting audit data across industries like finance and healthcare.
    4
    31 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides an immutable, tamper-evident audit trail for AI agents, enabling event logging with cryptographic chaining, search, verification, and statistics.
    2
    MIT