MCP server
Connect Claude Code and other MCP clients to Fishcamp. Endpoint, setup, and every tool with its arguments and an example.
Updated
Fishcamp has a built-in Model Context Protocol server. Connect an AI agent such as Claude Code and it can read channels, search messages, check audio rooms and transcripts, and post, all inside your workspace and under the name of an API key.
- Endpoint:
https://api.fishcamp.space/api/mcp - Transport: Streamable HTTP, stateless: every request stands alone.
- Authentication:
Authorization: Bearer cwk_…on every request.
Set it up
1. Create an API key
An admin creates the key in Settings → API keys: read lets the agent look around, write also lets it
post messages and create channels. The MCP server needs read: a key with only write gets 403. An
AI agent's key works too. See API keys.
2. Connect Claude Code
claude mcp add --transport http fishcamp https://api.fishcamp.space/api/mcp --header "Authorization: Bearer cwk_YOUR_KEY"- Add
--scope userto use Fishcamp in all your projects, not only the current one. - In Claude Code, run
/mcpto check thatfishcampis connected. - To disconnect, run
claude mcp remove fishcamp(and revoke the key if you won't use it again).
Other MCP clients
Clients that support remote servers take an entry like this one. The file and the top-level key depend on the client:
.mcp.json (Claude Code) and mcp.json (Cursor) use mcpServers; .vscode/mcp.json (VS Code) uses servers.
{
"mcpServers": {
"fishcamp": {
"type": "http",
"url": "https://api.fishcamp.space/api/mcp",
"headers": { "Authorization": "Bearer cwk_YOUR_KEY" }
}
}
}For clients that only start local (stdio) servers, such as Claude Desktop, bridge with
mcp-remote:
{
"mcpServers": {
"fishcamp": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.fishcamp.space/api/mcp", "--header", "Authorization:${FISHCAMP_AUTH}"],
"env": { "FISHCAMP_AUTH": "Bearer cwk_YOUR_KEY" }
}
}
}Chat apps whose connectors only sign in with OAuth, such as ChatGPT, can't use API keys.
Things to ask
Who's in the audio room in #platform right now?
Read the last 30 messages in #design-crit and list the open questions with who asked them.
Search for "rate limit" in #incidents and summarize what we decided.
Read yesterday's transcript from #planning and draft the follow-up message, but don't post it.
Create a channel called release-2-5 and post the checklist from RELEASE.md there.Messages the agent posts appear under the key's name with an AI label.
Tools
| Tool | What it does | Scope |
|---|---|---|
whoami | Shows the key's name and scopes, the workspace and who the key acts for | read |
list_channels | Lists the channels the key can see | read |
read_messages | Reads a channel's messages | read |
search_messages | Searches messages | read |
post_message | Posts a message | write |
create_channel | Creates a channel | write |
list_members | Lists the people in the workspace | read |
get_audio_room | Shows who's in a channel's audio room | read |
list_transcripts | Lists transcripts | read |
read_transcript | Reads one transcript | read |
Where a tool takes a channel, pass the channel's ID or its name, for example design-crit.
With an AI agent's key, the tools only see the agent's channels: list_channels,
search_messages and list_transcripts leave the others out, and any other channel answers "not found".
list_members still lists everyone in the workspace.
whoami
Returns the workspace the key belongs to, the person it acts for (the admin who created it) and the key's name and scopes. Call it first to check the connection. No arguments.
{}list_channels
Lists the channels the key can see, with their IDs, names and descriptions. No arguments.
{}read_messages
Reads the latest messages of a channel. To go further back, pass the cursor from the previous result as before.
| Argument | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel ID or name. |
limit | integer | No | How many messages, from 1 to 100. |
before | string | No | Cursor from a previous call, to read older messages. |
{ "channel": "design-crit", "limit": 20 }search_messages
Searches message text in the channels the key can see.
| Argument | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Words to look for. |
channel | string | No | Only search this channel (ID or name). |
limit | integer | No | Maximum number of results. |
{ "query": "rate limit", "channel": "incidents", "limit": 10 }post_message
Posts a message to a channel, under the key's name with an AI label. Needs write.
| Argument | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel ID or name. |
text | string | Yes | The message, up to 4,000 characters. |
{ "channel": "general", "text": "Release 2.5 is out. Notes: https://example.com/releases/2.5" }create_channel
Creates a channel. Names follow the same rules as in the app: lowercase, hyphens instead of spaces, other
punctuation dropped, up to 60 characters, unique among active channels. Needs write.
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Channel name. |
description | string | No | What the channel is for, up to 250 characters. |
private | boolean | No | true creates a private channel. Default: public. |
{ "name": "release-2-5", "description": "Checklist and status for release 2.5", "private": false }list_members
Lists the people in the workspace with their names, emails and roles. No arguments.
{}get_audio_room
Shows whether the channel's audio room is open, since when, who's in it and who's sharing a screen.
| Argument | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel ID or name. |
{ "channel": "platform" }list_transcripts
Lists recent transcripts, with when the call happened and what was decided for it.
| Argument | Type | Required | Description |
|---|---|---|---|
channel | string | No | Only transcripts from this channel (ID or name). |
limit | integer | No | Maximum number of transcripts. |
{ "channel": "planning", "limit": 5 }read_transcript
Reads one transcript: every line with its speaker, and the routing decision (discard, save to OpenSync or send to agents).
| Argument | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Transcript ID, from list_transcripts. |
{ "id": "3f2d9c1e-5b7a-4c2e-9a0b-6d8e1f4a2c7b" }Call it without a client
The server speaks JSON-RPC over HTTP, so you can test it with curl. List the tools:
curl -sS https://api.fishcamp.space/api/mcp \
-H "Authorization: Bearer cwk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'Call one:
curl -sS https://api.fishcamp.space/api/mcp \
-H "Authorization: Bearer cwk_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"read_messages","arguments":{"channel":"general","limit":5}}}'The answer is a JSON-RPC message; depending on the server's choice it arrives as plain JSON or as a server-sent event
(a data: {…} line).
Troubleshooting
| Symptom | Fix |
|---|---|
401 or "unauthorized" | The key is missing, mistyped or revoked. Check the header is exactly Authorization: Bearer cwk_…. |
| A write tool fails | The key only has read. Create a key with write. |
403 on every request | The key has no read scope. Create a key with read. |
| "not found" for a channel | Check the name or ID with list_channels. Private channels the key can't see look like they don't exist, and an agent's key only sees the agent's channels. |
Claude Code doesn't list fishcamp | Run claude mcp list. If you added it with the default scope, it only exists in the project where you ran the command. |