Agent unable to handle optional parameters for mcp server
Issue
Trying to connect a github mcp server to my agent. Running into issues where the function has optional parameters and these optional parameters are not explicitly set. If these optional parameters are set then the function is executed as expected. Tested this mcp server with claude and langchain, and both were able to call these functions without explicitly setting the optional parameters. Anything I'm missing?
CrewAI
Code:
try:
serverparams = StdioServerParameters(
command="docker",
args=["run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"],
env={"GITHUB_PERSONAL_ACCESS_TOKEN": ""},
)
with MCPServerAdapter(serverparams) as tools:
with open('/home/sebek/Dev/ai/crew_ai_custom/src/crew_ai_custom/config/agents.yaml', 'r') as agents_file:
agents_config = yaml.safe_load(agents_file)
with open('/home/sebek/Dev/ai/crew_ai_custom/src/crew_ai_custom/config/tasks.yaml', 'r') as tasks_file:
tasks_config = yaml.safe_load(tasks_file)
agent = Agent(
config=agents_config["debug_tool_executor"],
verbose=True,
tools=tools,
max_iter=5,
)
task = Task(
config=tasks_config["tool_task"],
)
output = agent.execute_task(task, {'tool': 'create_issue', 'parameters': {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}}, tools)
print(output)
except Exception as e:
raise Exception(f"An error occurred while running the crew: {e}")
Failed Output:
GitHub MCP Server running on stdio
# Agent: Tool executor
## Task: Execute the tool with the parameters provided by the user. Make sure to return the result of the tool execution. Do not add any additional parameters to the tool execution even if the passed in parameters are incorrect, or some are missing. Parameters: is a json object with the parameters to pass to the tool Tool: {tool} Parameters: {parameters}
args ()
kwargs {'assignees': None, 'body': None, 'labels': None, 'milestone': None, 'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}
# Agent: Tool executor
## Thought: Thought: I will execute the create_issue tool with the provided parameters.
## Using tool: create_issue
## Tool Input:
"{\"owner\": \"Sebek1232\", \"repo\": \"temp\", \"title\": \"test issue\"}"
## Tool Output:
parameter body is not of type string, is <nil>
# Agent: Tool executor
## Final Answer:
parameter body is not of type string, is <nil>
parameter body is not of type string, is <nil
Langchain
async def main():
server_params = StdioServerParameters(
command="docker",
args=["run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"],
env={"GITHUB_PERSONAL_ACCESS_TOKEN": ""},
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await load_mcp_tools(session)
agent = create_react_agent(model, tools)
agent_response = await agent.ainvoke({"messages": "create issue with these parameters {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}"})
print(agent_response)
if __name__ == "__main__":
asyncio.run(main())
Successful Output:
GitHub MCP Server running on stdio
{'messages': [HumanMessage(content="create issue with these parameters {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}", additional_kwargs={}, response_metadata={}, id='d85ce150-845f-433e-af21-e3e36c43b2d6'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Vlnbb3F4Rb4UpoVkzSRDV8Vi', 'function': {'arguments': '{"owner":"Sebek1232","repo":"temp","title":"test issue"}', 'name': 'create_issue'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 27, 'prompt_tokens': 3696, 'total_tokens': 3723, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BUzdBCszW0Hj1LKavOw8KrrcPeOT9', 'service_tier': 'default', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--629f4e75-ec94-48ff-9fb8-fefbf453f569-0', tool_calls=[{'name': 'create_issue', 'args': {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}, 'id': 'call_Vlnbb3F4Rb4UpoVkzSRDV8Vi', 'type': 'tool_call'}], usage_metadata={'input_tokens': 3696, 'output_tokens': 27, 'total_tokens': 3723, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='{"id":3049690911,"number":4,"state":"open","locked":false,"title":"test issue","author_association":"OWNER","user":{"login":"Sebek1232","id":67287582,"node_id":"MDQ6VXNlcjY3Mjg3NTgy","avatar_url":"https://avatars.githubusercontent.com/u/67287582?v=4","html_url":"https://github.com/Sebek1232","gravatar_id":"","type":"User","site_admin":false,"url":"https://api.github.com/users/Sebek1232","events_url":"https://api.github.com/users/Sebek1232/events{/privacy}","following_url":"https://api.github.com/users/Sebek1232/following{/other_user}","followers_url":"https://api.github.com/users/Sebek1232/followers","gists_url":"https://api.github.com/users/Sebek1232/gists{/gist_id}","organizations_url":"https://api.github.com/users/Sebek1232/orgs","received_events_url":"https://api.github.com/users/Sebek1232/received_events","repos_url":"https://api.github.com/users/Sebek1232/repos","starred_url":"https://api.github.com/users/Sebek1232/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Sebek1232/subscriptions"},"comments":0,"created_at":"2025-05-08T17:47:30Z","updated_at":"2025-05-08T17:47:30Z","url":"https://api.github.com/repos/Sebek1232/temp/issues/4","html_url":"https://github.com/Sebek1232/temp/issues/4","comments_url":"https://api.github.com/repos/Sebek1232/temp/issues/4/comments","events_url":"https://api.github.com/repos/Sebek1232/temp/issues/4/events","labels_url":"https://api.github.com/repos/Sebek1232/temp/issues/4/labels{/name}","repository_url":"https://api.github.com/repos/Sebek1232/temp","reactions":{"total_count":0,"+1":0,"-1":0,"laugh":0,"confused":0,"heart":0,"hooray":0,"rocket":0,"eyes":0,"url":"https://api.github.com/repos/Sebek1232/temp/issues/4/reactions"},"node_id":"I_kwDON37RWM61xpcf"}', name='create_issue', id='a83e1065-2563-47be-bc03-1af542968467', tool_call_id='call_Vlnbb3F4Rb4UpoVkzSRDV8Vi'), AIMessage(content='The issue has been created successfully! Here are the details:\n\n- **Title:** test issue\n- **Issue Number:** 4\n- **State:** Open\n- **Created By:** [Sebek1232](https://github.com/Sebek1232)\n- **Comments:** 0\n- **Created At:** May 8, 2025\n- **Link to Issue:** [View Issue](https://github.com/Sebek1232/temp/issues/4)\n\nIf you need anything else, feel free to ask!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 111, 'prompt_tokens': 4259, 'total_tokens': 4370, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 3712}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0392822090', 'id': 'chatcmpl-BUzdD9w4TWGLSSuKJwPjhJZNQwhWL', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--efad6c95-4508-4d00-b3d6-8a97a323525b-0', usage_metadata={'input_tokens': 4259, 'output_tokens': 111, 'total_tokens': 4370, 'input_token_details': {'audio': 0, 'cache_read': 3712}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}
@Sebek1232 could you describe what is the missing optional parameter?
@lucasgomide
for the tool create_issue of the github mcp server
this is the json schema outputted in the crewai logs
{
"properties": {
"assignees": {
"anyOf": [
{
"items": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
},
"type": "array"
},
{ "type": "null" }
],
"default": null,
"description": "Usernames to assign to this issue",
"title": ""
},
"body": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
],
"default": null,
"description": "Issue body content",
"title": ""
},
"labels": {
"anyOf": [
{
"items": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
},
"type": "array"
},
{ "type": "null" }
],
"default": null,
"description": "Labels to apply to this issue",
"title": ""
},
"milestone": {
"anyOf": [
{ "type": "number" },
{ "type": "null" }
],
"default": null,
"description": "Milestone number",
"title": ""
},
"owner": {
"description": "Repository owner",
"title": "",
"type": "string"
},
"repo": {
"description": "Repository name",
"title": "",
"type": "string"
},
"title": {
"description": "Issue title",
"title": "",
"type": "string"
}
},
"required": ["owner", "repo", "title"],
"title": "DynamicModel",
"type": "object"
}
as you can see the required parameters are ["owner", "repo", "title"] and everything else is optional with default values of null.
when I prompt the agent to create issue like so
output = agent.execute_task(task, {'tool': 'create_issue', 'parameters': {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue'}}, tools)
i get errors like so
crewai logs:
# Agent: Tool executor
## Thought: Thought: I need to create a new issue in the specified repository using the provided parameters.
## Using tool: create_issue
## Tool Input:
"{\"owner\": \"Sebek1232\", \"repo\": \"temp\", \"title\": \"test issue\"}"
## Tool Output:
parameter body is not of type string, is <nil>
error from docker logs of mcp server:
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"parameter body is not of type string, is \u003cnil\u003e"}],"isError":true}}
and when I explicitly set the body parameter to None
output = agent.execute_task(task, {'tool': 'create_issue', 'parameters': {'owner': 'Sebek1232', 'repo': 'temp', 'title': 'test issue', 'body': None}}, tools)
i get errors like so
crewai logs:
# Agent: Tool executor
## Using tool: create_issue
## Tool Input:
"{\"owner\": \"Sebek1232\", \"repo\": \"temp\", \"title\": \"test issue\", \"body\": null}"
## Tool Output:
parameter body is not of type string, is <nil>
error from docker logs of mcp server:
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"parameter body is not of type string, is \u003cnil\u003e"}],"isError":true}}
So in this case I can see that the agent correctly sends over a json null but I'm still getting the same error. Now I would say this is an issue with the MCP server as the server is written in go and nil is the go null type but when I hook up the same server to claude desktop or langchain and recreate the same scenario I am getting the expected output. So I do not know what is done differently in crewai vs the other frameworks/apps.
This is the case with other tools that have optional parameters. If I set all the optional parameters to non null values, crewai does correctly call the tool and create the issue.
Versions
name = "crewai"
version = "0.119.0"
name = "crewai-tools"
version = "0.44.0"
Models
# tested with both, same issue
llm: "openai/gpt-4o-mini"
llm: "anthropic/claude-3-7-sonnet-20250219"
amazing description, very useful. I'll debug that
@Sebek1232 I've encountered a few strange behaviors from the GitHub MPC server. Essentially, the server claims that the body can be either a "string" or null. However, once the field is present in the request payload, it shouldn’t be null...
I have a few ideas to address this and hope to bring something up in the next days.
The last thing, I'd recommend using Crew instead agent.execute_task
serverparams = StdioServerParameters(
command="docker",
args=[
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server",
],
env={
"GITHUB_PERSONAL_ACCESS_TOKEN": ""
},
)
with MCPServerAdapter(serverparams) as tools:
agent = Agent(
role="Github assistent ",
goal="create an issue on github",
backstory="You are a helpful assistant that will help me create an issue on github",
verbose=True,
tools=tools,
)
task = Task(
description="create an issue on github <YOUR ACCOUNT>/<YOUR REPO> about <SAY THE TOPIC/ISSUE>",
expected_output="the issue is created",
agent=agent,
)
crew = Crew(
agents=[agent],
tasks=[task],
verbose=True,
process=Process.sequential,
)
print(crew.kickoff())
Sounds good. I did some more experimenting with a different mcp server. The git mcp server which also has a tool with optional parameters that accepts null. Crewai agent was able to call this as expected. Here are the differences I found between this mcp server and the github mcp server thats causing issues
For the tool git_create_branch
mcp server docker logs response for list tools call:
{
"name": "git_create_branch",
"description": "Creates a new branch from an optional base branch",
"inputSchema": {
"properties": {
"repo_path": {
"title": "Repo Path",
"type": "string"
},
"branch_name": {
"title": "Branch Name",
"type": "string"
},
"base_branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Base Branch"
}
},
"required": [
"repo_path",
"branch_name"
],
"title": "GitCreateBranch",
"type": "object"
}
}
In crewai logs when printing the tool:
{
"properties": {
"repo_path": {
"description": "",
"title": "Repo Path",
"type": "string"
},
"branch_name": {
"description": "",
"title": "Branch Name",
"type": "string"
},
"base_branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "",
"title": "Base Branch"
}
},
"required": [
"repo_path",
"branch_name"
],
"title": "GitCreateBranch",
"type": "object"
}
So here for the base_branch parameter the schemas line up, both showing they accept null
For the github mcp server mcp server docker logs response for list tools call:
{
"description": "Create a new issue in a GitHub repository",
"inputSchema": {
"type": "object",
"properties": {
"assignees": {
"description": "Usernames to assign to this issue",
"items": {
"type": "string"
},
"type": "array"
},
"body": {
"description": "Issue body content",
"type": "string"
},
"labels": {
"description": "Labels to apply to this issue",
"items": {
"type": "string"
},
"type": "array"
},
"milestone": {
"description": "Milestone number",
"type": "number"
},
"owner": {
"description": "Repository owner",
"type": "string"
},
"repo": {
"description": "Repository name",
"type": "string"
},
"title": {
"description": "Issue title",
"type": "string"
}
},
"required": [
"owner",
"repo",
"title"
]
},
"name": "create_issue"
}
In crewai logs when printing the tool:
{
"properties": {
"assignees": {
"anyOf": [
{
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Usernames to assign to this issue",
"title": ""
},
"body": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Issue body content",
"title": ""
},
"labels": {
"anyOf": [
{
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Labels to apply to this issue",
"title": ""
},
"milestone": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Milestone number",
"title": ""
},
"owner": {
"description": "Repository owner",
"title": "",
"type": "string"
},
"repo": {
"description": "Repository name",
"title": "",
"type": "string"
},
"title": {
"description": "Issue title",
"title": "",
"type": "string"
}
},
"required": [
"owner",
"repo",
"title"
],
"title": "DynamicModel",
"type": "object"
}
In this case, the schemas don't line up, and the response from the mcp server does not say that it accepts null for the optional parameters, while the schema that crewai has does accept null parameters.
Looks like crewai adds to the schema that for optional parameters it accepts nulls, when it really shouldn't???
Hey @Sebek1232 yes it's really a python thing. In python optional are always a type | None and set to None but in other language like go this doesnt fly as with the github mcp server. We have worked and fixed the issue upstream in mcpadapt which crewAI tools use internally.
See https://github.com/grll/mcpadapt/pull/49 for the details.
Starting from version 0.1.9 of mcpadapt this should be fixed, you can try bumping it.
That's amazing @grll tks a lot for that! :blow: