Manifold Markets MCP Server
by bmorphism
- src
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
McpError,
ErrorCode,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
const API_BASE = 'https://api.manifold.markets';
const server = new Server(
{
name: 'manifold-markets',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
// Tool schemas
const CreateMarketSchema = z.object({
outcomeType: z.enum(['BINARY', 'MULTIPLE_CHOICE', 'PSEUDO_NUMERIC', 'POLL', 'BOUNTIED_QUESTION']),
question: z.string(),
description: z.union([
z.string(),
z.object({
type: z.literal('doc'),
content: z.array(z.any()),
})
]).optional(),
closeTime: z.number().optional(), // Unix timestamp in milliseconds
visibility: z.enum(['public', 'unlisted']).optional(),
initialProb: z.number().min(1).max(99).optional(),
min: z.number().optional(),
max: z.number().optional(),
isLogScale: z.boolean().optional(),
initialValue: z.number().optional(),
answers: z.array(z.string()).optional(),
addAnswersMode: z.enum(['DISABLED', 'ONLY_CREATOR', 'ANYONE']).optional(),
shouldAnswersSumToOne: z.boolean().optional(),
totalBounty: z.number().optional(),
groupIds: z.array(z.string()).optional(),
});
const SearchMarketsSchema = z.object({
term: z.string().optional(),
limit: z.number().min(1).max(100).optional(),
filter: z.enum(['all', 'open', 'closed', 'resolved']).optional(),
sort: z.enum(['newest', 'score', 'liquidity']).optional(),
});
const PlaceBetSchema = z.object({
marketId: z.string(),
amount: z.number().positive(),
outcome: z.enum(['YES', 'NO']),
limitProb: z.number().min(0.01).max(0.99).optional(),
});
const GetMarketSchema = z.object({
marketId: z.string(),
});
const GetUserSchema = z.object({
username: z.string(),
});
const GetUserPositionsSchema = z.object({
userId: z.string(),
});
const SellSharesSchema = z.object({
marketId: z.string(),
outcome: z.enum(['YES', 'NO']).optional(),
shares: z.number().optional(),
});
const AddLiquiditySchema = z.object({
marketId: z.string(),
amount: z.number().positive(),
});
const CancelBetSchema = z.object({
betId: z.string(),
});
const UnresolveMarketSchema = z.object({
contractId: z.string(),
answerId: z.string().optional(),
});
const CloseMarketSchema = z.object({
contractId: z.string(),
closeTime: z.number().int().nonnegative().optional(),
});
const AddAnswerSchema = z.object({
contractId: z.string(),
text: z.string().min(1),
});
const FollowMarketSchema = z.object({
contractId: z.string(),
follow: z.boolean(),
});
const AddBountySchema = z.object({
contractId: z.string(),
amount: z.number().positive().finite(),
});
const AwardBountySchema = z.object({
contractId: z.string(),
commentId: z.string(),
amount: z.number().positive().finite(),
});
const RemoveLiquiditySchema = z.object({
contractId: z.string(),
amount: z.number().positive().finite(),
});
const ReactSchema = z.object({
contentId: z.string(),
contentType: z.enum(['comment', 'contract']),
remove: z.boolean().optional(),
reactionType: z.enum(['like', 'dislike']).default('like'),
});
const SendManaSchema = z.object({
toIds: z.array(z.string()),
amount: z.number().min(10),
message: z.string().optional(),
});
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'create_market',
description: 'Create a new prediction market',
inputSchema: {
type: 'object',
properties: {
outcomeType: {
type: 'string',
enum: ['BINARY', 'MULTIPLE_CHOICE', 'PSEUDO_NUMERIC', 'POLL', 'BOUNTIED_QUESTION'],
description: 'Type of market to create'
},
question: {
type: 'string',
description: 'The headline question for the market'
},
description: {
type: 'string',
description: 'Optional description for the market'
},
closeTime: {
type: 'string',
description: 'Optional. ISO timestamp when market will close. Defaults to 7 days.'
},
visibility: {
type: 'string',
enum: ['public', 'unlisted'],
description: 'Optional. Market visibility. Defaults to public.'
},
initialProb: {
type: 'number',
description: 'Required for BINARY markets. Initial probability (1-99)'
},
min: {
type: 'number',
description: 'Required for PSEUDO_NUMERIC markets. Minimum resolvable value'
},
max: {
type: 'number',
description: 'Required for PSEUDO_NUMERIC markets. Maximum resolvable value'
},
isLogScale: {
type: 'boolean',
description: 'Optional for PSEUDO_NUMERIC markets. If true, increases exponentially'
},
initialValue: {
type: 'number',
description: 'Required for PSEUDO_NUMERIC markets. Initial value between min and max'
},
answers: {
type: 'array',
items: { type: 'string' },
description: 'Required for MULTIPLE_CHOICE/POLL markets. Array of possible answers'
},
addAnswersMode: {
type: 'string',
enum: ['DISABLED', 'ONLY_CREATOR', 'ANYONE'],
description: 'Optional for MULTIPLE_CHOICE markets. Controls who can add answers'
},
shouldAnswersSumToOne: {
type: 'boolean',
description: 'Optional for MULTIPLE_CHOICE markets. Makes probabilities sum to 100%'
},
totalBounty: {
type: 'number',
description: 'Required for BOUNTIED_QUESTION markets. Amount of mana for bounty'
}
},
required: ['outcomeType', 'question']
}
},
{
name: 'search_markets',
description: 'Search for prediction markets with optional filters',
inputSchema: {
type: 'object',
properties: {
term: { type: 'string', description: 'Search query' },
limit: { type: 'number', description: 'Max number of results (1-100)' },
filter: { type: 'string', enum: ['all', 'open', 'closed', 'resolved'] },
sort: { type: 'string', enum: ['newest', 'score', 'liquidity'] },
},
},
},
{
name: 'get_market',
description: 'Get detailed information about a specific market',
inputSchema: {
type: 'object',
properties: {
marketId: { type: 'string', description: 'Market ID' },
},
required: ['marketId'],
},
},
{
name: 'get_user',
description: 'Get user information by username',
inputSchema: {
type: 'object',
properties: {
username: { type: 'string', description: 'Username' },
},
required: ['username'],
},
},
{
name: 'place_bet',
description: 'Place a bet on a market',
inputSchema: {
type: 'object',
properties: {
marketId: { type: 'string', description: 'Market ID' },
amount: { type: 'number', description: 'Amount to bet in mana' },
outcome: { type: 'string', enum: ['YES', 'NO'] },
limitProb: {
type: 'number',
description: 'Optional limit order probability (0.01-0.99)',
},
},
required: ['marketId', 'amount', 'outcome'],
},
},
{
name: 'cancel_bet',
description: 'Cancel a limit order bet',
inputSchema: {
type: 'object',
properties: {
betId: { type: 'string', description: 'Bet ID to cancel' },
},
required: ['betId'],
},
},
{
name: 'sell_shares',
description: 'Sell shares in a market',
inputSchema: {
type: 'object',
properties: {
marketId: { type: 'string', description: 'Market ID' },
outcome: { type: 'string', enum: ['YES', 'NO'], description: 'Which type of shares to sell (defaults to what you have)' },
shares: { type: 'number', description: 'How many shares to sell (defaults to all)' },
},
required: ['marketId'],
},
},
{
name: 'add_liquidity',
description: 'Add mana to market liquidity pool',
inputSchema: {
type: 'object',
properties: {
marketId: { type: 'string', description: 'Market ID' },
amount: { type: 'number', description: 'Amount of mana to add' },
},
required: ['marketId', 'amount'],
},
},
{
name: 'get_positions',
description: 'Get user positions across markets',
inputSchema: {
type: 'object',
properties: {
userId: { type: 'string', description: 'User ID' },
},
required: ['userId'],
},
},
{
name: 'unresolve_market',
description: 'Unresolve a previously resolved market',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
answerId: { type: 'string', description: 'Optional. Answer ID for multiple choice markets' }
},
required: ['contractId']
}
},
{
name: 'close_market',
description: 'Close a market for trading',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
closeTime: { type: 'number', description: 'Optional. Unix timestamp in milliseconds when market will close' }
},
required: ['contractId']
}
},
{
name: 'add_answer',
description: 'Add a new answer to a multiple choice market',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
text: { type: 'string', description: 'Answer text' }
},
required: ['contractId', 'text']
}
},
{
name: 'follow_market',
description: 'Follow or unfollow a market',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
follow: { type: 'boolean', description: 'True to follow, false to unfollow' }
},
required: ['contractId', 'follow']
}
},
{
name: 'add_bounty',
description: 'Add bounty to a market',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
amount: { type: 'number', description: 'Amount of mana to add as bounty' }
},
required: ['contractId', 'amount']
}
},
{
name: 'award_bounty',
description: 'Award bounty to a comment',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
commentId: { type: 'string', description: 'Comment ID to award bounty to' },
amount: { type: 'number', description: 'Amount of bounty to award' }
},
required: ['contractId', 'commentId', 'amount']
}
},
{
name: 'remove_liquidity',
description: 'Remove liquidity from market pool',
inputSchema: {
type: 'object',
properties: {
contractId: { type: 'string', description: 'Market ID' },
amount: { type: 'number', description: 'Amount of liquidity to remove' }
},
required: ['contractId', 'amount']
}
},
{
name: 'react',
description: 'React to a market or comment',
inputSchema: {
type: 'object',
properties: {
contentId: { type: 'string', description: 'ID of market or comment' },
contentType: { type: 'string', enum: ['comment', 'contract'], description: 'Type of content to react to' },
remove: { type: 'boolean', description: 'Optional. True to remove reaction' },
reactionType: { type: 'string', enum: ['like', 'dislike'], description: 'Type of reaction' }
},
required: ['contentId', 'contentType']
}
},
{
name: 'send_mana',
description: 'Send mana to other users',
inputSchema: {
type: 'object',
properties: {
toIds: {
type: 'array',
items: { type: 'string' },
description: 'Array of user IDs to send mana to'
},
amount: { type: 'number', description: 'Amount of mana to send (min 10)' },
message: { type: 'string', description: 'Optional message to include' },
},
required: ['toIds', 'amount'],
},
},
],
}));
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'create_market': {
const params = CreateMarketSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
// Validate required fields based on market type
switch (params.outcomeType) {
case 'BINARY':
if (!params.initialProb) {
throw new McpError(
ErrorCode.InvalidParams,
'initialProb is required for BINARY markets'
);
}
break;
case 'PSEUDO_NUMERIC':
if (!params.min || !params.max || !params.initialValue) {
throw new McpError(
ErrorCode.InvalidParams,
'min, max, and initialValue are required for PSEUDO_NUMERIC markets'
);
}
break;
case 'MULTIPLE_CHOICE':
case 'POLL':
if (!params.answers || !Array.isArray(params.answers)) {
throw new McpError(
ErrorCode.InvalidParams,
'answers array is required for MULTIPLE_CHOICE/POLL markets'
);
}
break;
case 'BOUNTIED_QUESTION':
if (!params.totalBounty) {
throw new McpError(
ErrorCode.InvalidParams,
'totalBounty is required for BOUNTIED_QUESTION markets'
);
}
break;
}
// Convert string description to TipTap format if needed
if (typeof params.description === 'string') {
params.description = {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: params.description
}
]
}
]
};
}
const response = await fetch(`${API_BASE}/v0/market`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify(params),
});
if (!response.ok) {
const error = await response.text();
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${error}`
);
}
const market = await response.json();
return {
content: [{
type: 'text',
text: `Created market: ${market.url}`,
}],
};
}
case 'search_markets': {
const params = SearchMarketsSchema.parse(args);
const searchParams = new URLSearchParams();
if (params.term) searchParams.set('term', params.term);
if (params.limit) searchParams.set('limit', params.limit.toString());
if (params.filter) searchParams.set('filter', params.filter);
if (params.sort) searchParams.set('sort', params.sort);
const response = await fetch(
`${API_BASE}/v0/search-markets?${searchParams}`,
{ headers: { Accept: 'application/json' } }
);
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const markets = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(markets, null, 2),
},
],
};
}
case 'get_market': {
const { marketId } = GetMarketSchema.parse(args);
const response = await fetch(`${API_BASE}/v0/market/${marketId}`, {
headers: { Accept: 'application/json' },
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const market = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(market, null, 2),
},
],
};
}
case 'get_user': {
const { username } = GetUserSchema.parse(args);
const response = await fetch(`${API_BASE}/v0/user/${username}`, {
headers: { Accept: 'application/json' },
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const user = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(user, null, 2),
},
],
};
}
case 'place_bet': {
const params = PlaceBetSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/bet`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
contractId: params.marketId,
amount: params.amount,
outcome: params.outcome,
limitProb: params.limitProb,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
case 'cancel_bet': {
const { betId } = CancelBetSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/bet/cancel/${betId}`, {
method: 'POST',
headers: {
Authorization: `Key ${apiKey}`,
},
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Bet cancelled successfully',
},
],
};
}
case 'sell_shares': {
const params = SellSharesSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.marketId}/sell`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
outcome: params.outcome,
shares: params.shares,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
case 'add_liquidity': {
const params = AddLiquiditySchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.marketId}/add-liquidity`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
amount: params.amount,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Liquidity added successfully',
},
],
};
}
case 'get_positions': {
const { userId } = GetUserPositionsSchema.parse(args);
const response = await fetch(
`${API_BASE}/v0/bets?userId=${userId}`,
{ headers: { Accept: 'application/json' } }
);
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const positions = await response.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(positions, null, 2),
},
],
};
}
case 'unresolve_market': {
const params = UnresolveMarketSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/unresolve`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify(params),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Market unresolved successfully',
},
],
};
}
case 'close_market': {
const params = CloseMarketSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.contractId}/close`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
closeTime: params.closeTime,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Market closed successfully',
},
],
};
}
case 'add_answer': {
const params = AddAnswerSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.contractId}/answer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
text: params.text,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
const result = await response.json();
return {
content: [
{
type: 'text',
text: `Answer added with ID: ${result.newAnswerId}`,
},
],
};
}
case 'follow_market': {
const params = FollowMarketSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/follow-contract`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
contractId: params.contractId,
follow: params.follow,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: params.follow ? 'Now following market' : 'Unfollowed market',
},
],
};
}
case 'add_bounty': {
const params = AddBountySchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.contractId}/add-bounty`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
amount: params.amount,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Bounty added successfully',
},
],
};
}
case 'award_bounty': {
const params = AwardBountySchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.contractId}/award-bounty`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
commentId: params.commentId,
amount: params.amount,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Bounty awarded successfully',
},
],
};
}
case 'remove_liquidity': {
const params = RemoveLiquiditySchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/market/${params.contractId}/remove-liquidity`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
amount: params.amount,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Liquidity removed successfully',
},
],
};
}
case 'react': {
const params = ReactSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/react`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify(params),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: params.remove ? 'Reaction removed' : 'Reaction added',
},
],
};
}
case 'send_mana': {
const params = SendManaSchema.parse(args);
const apiKey = process.env.MANIFOLD_API_KEY;
if (!apiKey) {
throw new McpError(
ErrorCode.InternalError,
'MANIFOLD_API_KEY environment variable is required'
);
}
const response = await fetch(`${API_BASE}/v0/managram`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Key ${apiKey}`,
},
body: JSON.stringify({
toIds: params.toIds,
amount: params.amount,
message: params.message,
}),
});
if (!response.ok) {
throw new McpError(
ErrorCode.InternalError,
`Manifold API error: ${response.statusText}`
);
}
return {
content: [
{
type: 'text',
text: 'Mana sent successfully',
},
],
};
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
if (error instanceof z.ZodError) {
throw new McpError(
ErrorCode.InvalidParams,
`Invalid parameters: ${error.errors
.map((e) => `${e.path.join('.')}: ${e.message}`)
.join(', ')}`
);
}
throw error;
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Manifold Markets MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});